Lazy Flexslider for WordPress

Carousels/sliders are one of the most widely used widgets in development for the Web today (although the debate over using them still varies in opinion). In my experience w/sliders there’s literally no need to load every single image for a slider on init window load. Today I share my implementation for a client project brought to me by Staple Web Design using WordPress and Flexslider (which does not support lazy loading out of the box).

Uploading Media

In this particular case, the client can use the Media Uploader found on a post’s editor screen. As you can see in figure 1 the post allows multiple images to be inserted into the custom post we’ve created. Once the images are within the post as attachments we can run a loop in our PHP to grab the images and display them within our desired markup pattern.


Figure 1 : Uploading images via WordPress’ post editor screen

Retrieving Media

The PHP seen in figure 2 is placed within single-rvs.php which is a custom post type (called ‘rvs’) template. We use the get_children() method to grab all the image attachments for the post, grab our image sources and finally inject them accordingly. In order to display the thumbnails portion we must duplicate the top slider markup so we can sync both sections as documented by Flexslider. Now we can grab the “full” size image for the main slider (large single images) and for the carousel (smaller thumbnail images) we retrieve the “medium” size. This is one of the benefits of using WordPress’ Media Uploader as it will generate different sizes for each image uploaded. Using this approach we can use smaller files for the thumbnails and larger images for the main showcase slides. Makes sense right?

<section class="rv-media">
  <?php
  $images =& get_children( array (
    'post_parent'    => $post->ID,
    'post_type'      => 'attachment',
    'post_mime_type' => 'image'
  ));

  if ( ! empty($images) ) {
    // slider
    echo '<div id="slider" class="flexslider"><ul class="slides">';
    foreach ( $images as $attachment_id => $attachment ) {
      $image_attributes = wp_get_attachment_image_src( $attachment_id, 'full' );
      echo '<li><img src="//placehold.it/600x300/fff/111&text=Loading..." data-src="';
      echo $image_attributes[0];
      echo '" class="lazy" alt=""></li>';
    }
    echo '</ul></div>';

    // carousel
    echo '<div id="carousel" class="flexslider"><ul class="slides">';
    foreach ( $images as $attachment_id => $attachment ) {
      $image_attributes = wp_get_attachment_image_src( $attachment_id, 'medium' );
      echo '<li><img src="//placehold.it/300x250/fff/111&text=Loading..." data-src="';
      echo $image_attributes[0];
      echo '" class="lazy" alt=""></li>';
    }
    echo '</ul></div>';
  }
  ?>
</section>
Figure 2 : retrieving image attachments for single-rvs.php template

Lazy Loading Media

In the bottom of our document (just before the closing body tag) we load the Flexslider library followed by the custom config. The actual call for the entire config is wrapped in a window.load event so we can make sure the DOM is loaded as quickly as possible and not blocking page load. This way we create a perceived performance improvement for users by loading only a few images at once and lazily loading the rest after the window load event occurs.

<?php if ( get_post_type() == 'rvs' ) : ?>
<script src="<?php echo get_template_directory_uri(); ?>/js/jquery.flexslider.min.js?v2.2.2" defer></script>
<script src="<?php echo get_template_directory_uri(); ?>/js/flexslider-config.js"></script>
<?php endif; ?>
Figure 3 : Loading Flexslider lib and config options. Loads scripts for the template w/a custom post type called ‘rvs’

Within our config we call window.load and then a call to the Flexslider using some keys given to us by the Flexslider API and of course set our options accordingly. The following will only load the first two main gallery images at full size and finally load the first 3 thumbnails as medium image attachments lazily. Each time a slide advances it loads the next slide image in the series. This way we pre-load ahead of time and make the experience that much faster for the user.

function initSlides(slider) { // Fires when the slider loads the first slide
  var slide_count = slider.count - 1;

  jQuery(slider)
    .find('img.lazy:eq(0), img.lazy:eq(1)')
    .each(function() {
      var src = jQuery(this).attr('data-src');
      jQuery(this).attr('src', src).removeAttr('data-src');
    });
}

function initCarSlides(slider) { // Fires when the slider loads the first slide
  var amt   = slider.count,
  car_count = jQuery(slider).find("img.lazy").slice(0, 4);

  car_count.each(function () {
  var src = jQuery(this).attr("data-src");
    jQuery(this).attr("src", src).removeAttr("data-src").removeClass("lazy");
  });
}

function loadSlides(slider) { // Fires asynchronously with each slider animation
  var slides     = slider.slides,
      index      = slider.animatingTo,
      $slide     = jQuery(slides[index]),
      $img       = $slide.find('img[data-src]'),
      current    = index,
      nxt_slide  = current + 1,
      prev_slide = current - 1;

  $slide
    .parent()
    .find('img.lazy:eq('   current   '), img.lazy:eq('   prev_slide   '), img.lazy:eq('   nxt_slide   ')')
    .each(function() {
      var src = jQuery(this).attr('data-src');
      jQuery(this).attr('src', src).removeAttr('data-src');
    });
}

jQuery(window).load(function() {
  // Since we're syncing the carousel it req. init first
  jQuery('#carousel').flexslider({
    animation: "slide",
    controlNav: false,
    animationLoop: false,
    slideshow: false,
    itemWidth: 200,
    asNavFor: '#slider',
    move: 4,
    // Fires when the slider loads the first slide
    start: function(slider) {
      initCarSlides(slider);
    },
    // Fires asynchronously with each slider animation
    before: function(slider) {
      initCarSlides(slider);
    }
  });

  jQuery('#slider').flexslider({
    animation: "fade",
    touch: true,
    slideshow: false,
    controlNav: false,
    sync: "#carousel",
    slideshowSpeed: 7000,
    animationSpeed: 600,
    initDelay: 0,
    // Fires when the slider loads the first slide
    start: function(slider) {
      initSlides(slider);
    },
    // Fires asynchronously with each slider animation
    before: function(slider) {
      loadSlides(slider);
    }
  });
});
Figure 4 : Flexslider config for lazy loading

The final product

The following is a screenshot of my final prototype sent over for approval. As you can see, we grab all the posts attachments and inject them into our slider (lazily of course).

Figure 5 : Final result of the lazy loading Flexslider for WordPress

References

Dennis Gaebel

Design Technologist passionate for Open Source, SVG, Typography, Web Animation, Interaction Development & Pattern Based Design. http://droidpinkman.io.
  1. ❧ Chuck shouted:
    2014/10/08 • 11:49 pm

    Fantastic way of getting lazy load to work with Flexslider! Thanks for sharing!

  2. NP Chuck! I hope it comes in helpful. Cheers!

Leave a Reply

Your email address will not be published. Required fields are marked *

show formatting examples
<pre class="language-[markup | sass | css | php | javascript | ruby | clike | bash]"><code>
…code example goes here…
</code></pre>

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Comment Preview

  1. John Doe shouted this comment preview:
    2014/09/17