How do I enable HTML5 prefetching on this page?

If I understand correctly you are putting that code in header.php, that is required in the file single-dress.php using get_header(), and in the same file, after about 2 dozen lines, you define the variables you used 2 dozen lines before.

So, either your server has an embedded time machine, or your code can’t work.

What you have to do is just define variables before you use them.

Probably, instead of messing up your code, and fill your header with a series of if / elseif, you can separate your logic from your templates.

I’ll write a simple class that handles the prefetching. You can save it in a separate file and require it from your functions.php.

Please read the inline comments for more explanation.

<?php
class My_Theme_Prefetcher {

    public $next_url;
    public $prev_url;

    function setup() {
        global $wp_query;
        // for empty query there is nothing to prefetch 
        if ( ! $wp_query instanceof WP_Query || $wp_query->found_posts <= 0  ) return;
        if ( $wp_query->is_archive() ) {
            // next page URL for archives, that is empty if there is no next page URL
            $this->next_url = get_next_posts_page_link();
        } elseif ( $wp_query->is_singular( 'dress' ) ) {
            // if on a single page view for dress CPT, run a query for dress posts
            $this->set_dress_query( $wp_query->get_queried_object_id() );
        }
        // A filter to easily get class instance and access to instance methods and vars
        add_filter( 'my_theme_prefetcher', function() { return $this; } );
        // output the prefetch strings on wp_head
        add_action( 'wp_head', array( $this, 'output' ) );
    }

    /**
    * Perform a query to get all dresses in same collection,
    * save adjacent post URLs into instance properties
    */
    function set_dress_query( $dressid ) {
        $args =  static::dress_query_args( $dressid );
        if ( empty( $args ) ) return;
        $dress_query = new WP_Query( $args );
        if ( $dress_query->found_posts > 0 ) {
            // some dresses found, discover and save adjacent URLs
            $adjacents = static::get_adjacents( $dressid, $dress_query->posts );
            if ( $adjacents['next'] ) 
                $this->next_url = get_permalink( $adjacents['next'] );
            if ( $adjacents['prev'] ) 
                $this->prev_url = get_permalink( $adjacents['prev'] );
        }
    }

    /**
    * Given a current ID and a set of posts, discover adjacent posts
    */
    static function get_adjacents( $current, Array $all ) {
        $adjacents = array( 'prev' => FALSE, 'next' => FALSE );
        if ( is_numeric( $current ) && ! empty( $all ) ) {
            $ids = wp_list_pluck( $all, 'ID' );
            $this_posts = array_search( $current, $ids );
            if ( $this_posts !== FALSE ) {
                $prev_i = $this_posts > 0 ? $this_posts -1 : FALSE;
                $next_i = $this_posts < count( $ids ) - 1 ? $this_posts + 1 : FALSE;
                $previous = $prev_i !== FALSE ? $all[ $prev_i ] : FALSE;
                $next = $next_i !== FALSE ? $all[ $next_i ] : FALSE;
                $adjacents = array( 'prev' => $previous, 'next' => $next );
            }
        }
        return $adjacents;
    }

    /**
    * Output prefetch string on wp_head. Do nothing if no, or invalid, URL is set
    */
    function output() {
        if (
            empty( $this->next_url ) ||
            ! filter_var( $this->next_url, FILTER_VALIDATE_URL )
        ) return;
        $format="<link rel="prefetch" href="https://wordpress.stackexchange.com/questions/138778/%1$s"><link rel="prerender" href="https://wordpress.stackexchange.com/questions/138778/%1$s">";
        printf( $format, esc_url( $this->next_url ) );
    }

    /**
    * Returns the args for dress query for a given dress id.
    */
    static function dress_query_args( $dressid ) {
        $collections = get_the_terms( $dressid, 'collections' );
        if ( is_wp_error( $collections ) || empty( $collections ) ) return;
        $term = array_shift( $collections );
        $args = array(
            'post_type' => 'dress',
            'posts_per_page' => -1,
            'tax_query' => array(
                array( 'taxonomy' => 'collections', 'terms' => array( $term->term_id ) )
            ),
            'order' => 'ASC',
            'orderby' => 'title'
        );
        return $args;
    }

}

All the logic is in the class: when setup() method is called, the class looks at the current query, and if it is an archive, then setup next URL to next archive page. If the page is for a single dress view, then it runs a query to get all dresses in the same collection, and if found, save adjacent post URLs in instance variables.

Now we need to launch the class, i.e. call the setup() method on a hook fired after the main query is set, but before wp_head() is called: 'template_include' will be perfect.

I don’t use template_redirect here to allow faster redirect if needed (without triggering the additional query). However, 'template_include' is a filter, so we have to return the current template. Add to your functions.php:

add_filter( 'template_include', function( $template ) {
    $prefetcher = new My_Theme_Prefetcher;
    $prefetcher->setup();
    return $template;
} );

Now, you have only to be sure in your header.php there is the wp_head() call and the prefetch links will be added by the class.

There is another task to do. In template file (single-dress.php) we need to display adjacent posts links, but we don’t need to run another query, because adjacent posts URLs are already saved in the My_Theme_Prefetcher instantiated on 'template_include'.

So we need to access that instance and the custom filter 'my_theme_prefetcher' that was created for the scope.

So in the template, you can:

<figure class="pinboard">
    <?php

    // here goes your thumbnail image stuff, I will not copy it...

    // get the prefetcher instance and check it
    $prefetcher = apply_filters( 'my_theme_prefetcher', NULL );

    if ( $prefetcher instanceof My_Theme_Prefetcher ) { 
        // we got prefetcher instance, get the urls
        $prev_url = $prefetcher->prev_url ? : FALSE;
        $next_url = $prefetcher->next_url ? : FALSE;
    } else {
        // something goes wrong, try to run the query
        // function that returns arguments is static, so we can access it
        $args = My_Theme_Prefetcher::dress_query_args( $post->ID );
        $dress_by_tax = ! empty( $args ) ? new WP_Query( $args ) : FALSE;
        if ( $dress_by_tax instanceof WP_Query && ! empty( $dress_by_tax->posts ) ) {
            // function that returns adjacent posts is static, so we can access it
            $adjacents = My_Theme_Prefetcher::get_adjacents( $post->ID, $dress_by_tax->posts );
            $next_url = $adjacents['next'] ? get_permalink( $adjacents['next'] ) : FALSE;
            $prev_url = $adjacents['prev'] ? get_permalink( $adjacents['prev'] ) : FALSE;
        } else {
            $next_url = $prev_url = FALSE;
        }
    }
    ?>

    <?php if ( $next_url ) : ?>
        <div class="nav-next arrow block button spaced ribbon">
            <a href="https://wordpress.stackexchange.com/questions/138778/<?php echo $next_url ?>">Next</a>
        </div>
    <?php endif; ?>

    <?php if ( $prev_url ) : ?>
        <div class="nav-prev arrow-left block button spaced ribbon2 left">
            <a href="<?php echo $prev_url ?>">Previous</a>
        </div>
    <?php endif; ?>

    <div class="mask"><span>Click for larger size</span></div>

</figure>