Conditional loading of images for WordPress

Optimizing performance is a key point when designing and coding for mobile devices. Serving desktop sized images to a mobile device is a waste of bandwidth and time and should be avoided. While working on a project lately I came across a nice pragmatic technique to conditionally load different sized images named Source Shuffling. In this post I will show you how to combine this technique with WordPress post-thumbnails. Together with some smart compression of your images this may help you achieve an acceptable level of performance for your responsive project.

Source shuffling

The idea of ‘source-shuffling’ is to tie the value of the src-attribute of an image to a media-query defined in your CSS. When the dimensions of the browserwindow change and another media-query becomes ‘active’, the value of the scr-attribute is changed with javascript and an optimized image is served.

Preparing the media queries

If we want to know which media query is the active one we wil need some sort of ‘handle’ which we can act upon. We can create this handle by inserting a unique string into the page with the CSS pseudo-element ‘:after’ for each media-query in our CSS.

Let’s assume our design has two breakpoints. The first at 43.4375em (695px) and the second at 56.25em (900px). For screens less then 695px wide we will insert the string ‘small’ into the page and set it’s display property to ‘none’. For screens between 695px – 900px wide we will insert the string ‘medium’ and for screens larger than 900px we will insert the string ‘large’.

To prevent the strings showing up in each others queries we will specify a min-width and a max width. This way we can be sure that our ‘handle’ will only be present for the screenwidth we are targeting. The media-queries with our ‘handles’ will look like this:


@media only screen and (min-width: 0.1em) and (max-width: 43.4375em) {

/* -- HANDLE FOR CONDITIONAL LOADING OF IMAGES -- */
 body:after {
  content: "small";
  display: none;
 }
}

@media only screen and (min-width: 43.4375em) and (max-width:56.25em) {

/* -- HANDLE FOR CONDITIONAL LOADING OF IMAGES -- */
 body:after {
  content: "medium";
  display: none;
 }
}

@media only screen and (min-width: 56.25em) {

/* -- HANDLE FOR CONDITIONAL LOADING OF IMAGES -- */
 body:after {
  content: "large";
  display: none;
 }
}

Choosing image sizes

The sizes you choose for your images will depend upon the width’s specified in your media queries and the layout of your design at different screen sizes. To determine the right image sizes for a design I find it helpfull to resize the browserwindow and use the devtools to inspect the maximum width of an image for each media-query. That maximum width will be the width of my optimized image. Let’s assume we have come up with the following image sizes:

  • default: 240 x 180 px
  • medium: 500 x 375 px
  • large: 760 x 570 px

We can instruct WordPress to automatically generate these sizes each time an image is uploaded. The following code will activate post-thumbnails for WordPress and add our image-sizes.


// Add support for post thumbnails
function ts_enable_post_thumbnails() {
 if ( function_exists( 'add_theme_support' ) ) {
  add_theme_support( 'post-thumbnails' );

  add_image_size( 'image_s', 240, 180 );
  add_image_size( 'image_m', 500, 375 );
  add_image_size( 'image_l', 760, 570 );
 }
}
add_action( 'after_setup_theme', 'ts_enable_post_thumbnails' );

Storing the paths into data-attributes

We will use the HTML5 data attributes in our markup to store the paths to the different images. To keep the template clean I have build the content using a function inside the functions.php file. In the template we only need to call this function.


function ct_show_projects_list() {
  global $post;

  $query = new WP_Query( array(
    'post_type' => 'project'
    )
  );

  $output = '<ul class="projects-list">' . "n"; while ( $query->have_posts() ) : $query->the_post();

  if ( has_post_thumbnail() ) {
     // Retrieve the paths to the post-thumbnails attached to this post
     $thumb_s = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'image_s' );
     $thumb_m = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'image_m' );
     $thumb_l = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'image_l' );

     $thumb_url_s = $thumb_s['0'];
     $thumb_url_m = $thumb_m['0'];
     $thumb_url_l = $thumb_l['0'];

     $output .= "\t" . '<li class="project project-small">' . "\n";
     $output .= "\t\t" . '<a title="' . get_post( get_post_thumbnail_id() )->post_excerpt . '" href="' . get_permalink() . '">' . get_post( get_post_thumbnail_id() )->post_excerpt . '</a>' . "\n";
     $output .= "\t\t" . '<img alt="" src="' . $thumb_url_s . '" data-small="' .  $thumb_url_s . '" data-medium="' . $thumb_url_m_l . '" data-large="' . $thumb_url_m_l . '" />' . "\n";
     $output .= "\t\t" . '<div class="project-description">' . "\n";
     $output .= "\t\t\t" . '<h3>' . "\n";
     $output .= "\t\t\t\t" . get_post( get_post_thumbnail_id() )->post_excerpt . "\n";
     $output .= "\t\t\t" . '</h3>' . "\n";
     $output .= "\t\t" . '</div>' . "\n";
     $output .= "\t" . '</li>' . "\n";
   }

   endwhile;
   wp_reset_query();
   wp_reset_postdata();

   $output .= '</ul>' . "\n";
   echo $output;
 }


<section class="projects">
 <?php if ( function_exists( 'ct_show_projects_list' ) ) { ct_show_projects_list(); } ?>
</section>

In the code above you can see that we store the paths to the different sized thumbnails in the variables $thumb_url_s (small), $thumb_url_m (medium) and $thumb_url_l (large). The path to the small image will be our default value for the src. When the user has javascript disabled, this image will be served. The two other paths are asigned to the attributes data-medium and data-large. With these attributes we can store custom data for the page. I have named them ‘data-small’, ‘data-medium’ and ‘data-large’ but you can name them anything you wish as long as it is at least one character long and lowercase. Just prepend data- to your chosen name. If you want to know more about the data-attribute, this is a good place to start.

Making it work with jQuery

With all the preperations finished we can now use some jquery to dynamically test which media-query is active and then change the src-attribute to serve our optimized images.


jQuery( document ) .ready( function( $ ) {

  // Loads different images based on which media-query is active.
  var conditionalImageSwap = function() {
    var elem = document.body,
        pseudoElem = ':after',
        property = 'content',
        size = '',
        newSize = '',
        imgMedium = '',
        srcOriginal = '',
        srcMedium = '',
        imgLarge = '',
        srcLarge = '';

    /* Get the value of the inserted pseudo-element */
    function getPseudoElemContent() {
      size = window.getComputedStyle( elem, pseudoElem ) .getPropertyValue( property );
      removeQuotes();
    }

    /* Remove quotes from string if present. Needed for FF and Opera */
    function removeQuotes() {
      if( size.indexOf( "\"" ) != -1 ||
          size.indexOf( "\'" ) != -1 ) {
          size = size.replace( /['"]/g, "" );
      }
      changeSrcAttr();
    }

    /* Retrieve the value of the data-attributes and change the src attributes */
    function changeSrcAttr() {
      switch( size ) {
        case 'small':
          imgSmall = $( '[data-small]' );

          imgSmall .each( function( index ) {
            srcSmall = $( this ) .data( 'small' );
            $( this ) .attr( 'src', srcSmall );
          });

          break;

        case 'medium':
          imgMedium = $( '[data-medium]' );

          imgMedium .each( function( index ) {
            srcMedium = $( this ) .data( 'medium' );
            $( this ) .attr( 'src', srcMedium );
          });

          break;

        case 'large':
          imgLarge = $( '[data-large]' );

          imgLarge .each( function( index ) {
            srcLarge = $( this ) .data( 'large' );
            $( this ) .attr( 'src', srcLarge );
          });

          break;
      }
    }

    /* Check if the active media-query has changed after resizing the window */
    function checkValueOnResize() {
      $( window ) .resize( function() {
        newSize = window.getComputedStyle( elem, pseudoElem ) .getPropertyValue( property );

        if( size != newSize ) {
          getPseudoElemContent();
        }
      });
    }

    return {
      init: getPseudoElemContent,
      checkValueOnResize: checkValueOnResize
    };

  }();
  conditionalImageSwap.init();
  conditionalImageSwap.checkValueOnResize();

Determine the active media-query

To test which media-query is the active one we will retrieve the content of the ‘handle’ we added to our media-query earlier. This can be done with the GetComputedStyle() and getPropertyValue() methods. When we pass these the name of the element, the pseudo-element and the css property it will return us the final used css value for that property. In this case it will return the string ‘small’, ‘medium’ or ‘large’ which we will store in the variable named ‘size’.

I found that the browsers Firefox and Opera return the value wrapped inside quotes as opposed to chrome which only returns the content of the property. To make it work for at least Chrome, Firefox and Opera we will take the stored value and test if it contains quotes. If it does then we will remove them.

We now have a way to test for the active media-query because we know our page will contain the handle ‘small’ if our small media-query is active and the same for the ‘medium’ and ‘large’ media-queries. We can act upon these different cases with a switch statement.

Changing the src-attribute

Because we stored the different paths to the images using data attributes they are now easily available to us using the .data() method provided by jquery. We simply pass this method the names we gave them. So to change the src of the images we select the images, loop through them, retrieve the path with the data()-method and assign this path to the src attribute of the image.

Resizing the window

What if the browserwindow is resized? This may happen for example when the orientation of a handheld device is changed from portrait to landscape. We need a way to catch this event and test if our ‘handle’ has changed for example from ‘small’ to ‘medium’. jQuery’s resize() method provides a solution for this and is automatically fired when the screen is resized. We can use this inside of our checkValueOnResize function. This function tests if the ‘handle’ has changed. If that is the case then the new ‘handle’ is asigned to the variable ‘size’.

That’s it. We are now able to serve different images depending upon the screen size.

1 comment

  1. [ingiltere vizesi]

    31-05-2013

    My brother suggested I would possibly like this website. He was totally right. This put up actually made my day. You can not imagine simply how much time I had spent for this info! Thank you!

Comments are closed.