Build faster WordPress sites with lazy loading

Discover how lazy loading enables the inclusion of rich graphical media in a website without sacrificing page performance.

The continued growth of the web has led developers and content producers to cram as much into web pages as they can. High-resolution video, interactive advertisements, high-density graphics, rich visitor analytics – these can all be found on a regular web page. As consumer bandwidth and desktop capabilities increase, so does the footprint of any particular website.

At the same time, visitors are beginning to browse the web over mobile connections. This migration of site traffic from high to lower bandwidth is in direct opposition to the producers' goals of including more in their sites. New solutions are needed to combat and resolve this problem.

Lazy loading – only loading embedded assets such as high-resolution graphics and video when absolutely necessary – is one of the most straightforward solutions available to tackle this issue. It takes a little planning and finesse on the part of the development team, but can make a whole world of difference to a site's load time and overall performance.

Anything that loads outside of the main browser viewport can be loaded lazily. However, instead of optimising all the things, let's focus on two pieces of low-hanging fruit: images and video.

Loading images

A highly engaging site is rich with graphical content. News articles and blog posts in particular are easier to consume when paired with illustrative images. Unfortunately, embedded graphics must be processed in line with the rest of the page. Higher-resolution images will load slowly and detract from the visitor experience.

Instead, we can selectively load only the images visible in the device viewport. To do so, we change our markup to replace the src attributes of our standard <img /> tags with a data-lazy attribute. The image's source reference then points instead to a small placeholder image.

<img src="img/placeholder.png" data-lazy="img/horses.hidpi.png" />

When the page loads, the browser will only download the placeholder image. This should be a very simple image of the fewest colours possible. The placeholder will be cached by the browser and reused, resulting in a single HTTP request to build out imagery throughout the page.

JavaScript routine

We will build a JavaScript routine into the bottom of our page to process all of the lazy-loaded images and only present the ones we want to see.

The first part of our JavaScript-based lazy loading routine is a function that can determine whether or not a given image element is in the viewport. We will include the 300 pixels immediately above and below the viewport as a loading threshold so any user scroll events will reveal already-loaded images rather than our placeholder.

function inView( image ) {
    var $image = $( image ),
        view_top = $window.scrollTop() - 300,
        view_bottom = view_top + $window.height() + 600,
        height = $image.height(),
        _top = $image.offset().top,
        _bottom = _top + height;
    return height > 0 && _top <= view_bottom && _bottom >= view_top;

If the page loads from the top, any images within 300 pixels of the bottom of the viewport will be loaded automatically. If the page loads somewhere in the middle – like when a visitor visits a URL with a hash – images within 300 pixels of either the top or bottom of the viewport will load.

Visible images

Next, we will define the function that actually loads visible images. This function will grab the data-lazy attribute and use that to replace each tag's src attribute. The browser automatically detects the changes to the DOM, downloads the new assets, and replaces our placeholders in the page.

function maybeLoad( images ) {
    images.each( function( i, image ) {
        if ( image.hasAttribute( ‘data-lazy’ ) && inView( image ) ) {
            image.src = image.getAttribute( ‘data-lazy’ );
            image.removeAttribute( ‘data-lazy’ );
            image.trigger( ‘lazy-load’ );
    } );

To improve the maybeLoad() function, we can automatically update the images collection to remove newly loaded images from the array. This reduces both the application's memory footprint and the number of elements through which we need to iterate each time.

Loading threshold

As visitors scroll the page, we should also re-scan for images that have moved within our loading threshold. As the window's scroll event fires several times while the page is still scrolling, the event handler is throttled to prevent overflowing simpler devices' memories or overtaxing their processors.

$( window ).on( ‘scroll’, function() {
  if ( undefined === throttle_id ) {
  throttle_id = window.setTimeout(
    function() {
      maybeLoad( images );
      throttle_id = undefined;
} );

The event above will only fire once every 250 milliseconds, allowing us to continuously scan the page as the browser scrolls. Reducing the throttle speed will ensure even fast scrolling will never reveal an unloaded image. However, it might negatively impact slow scrolling by demanding more system resources to manage the event.

Loading video

Video assets can also drag down a page's performance significantly. When a video is embedded in the page using a script tag, the browser processes the script tag immediately, usually loads an iFrame in its place, and then begins downloading a large Flash (or Silverlight or MP4) resource.

This can lock the browser's interface and result in a visitor bouncing off your site to another. One technique to avoid this scenario is to force videos to display in a modal overlay – a lightbox – instead of in-line with the rest of your page content.

Two elements

Instead of a script embed from, say, YouTube, your page's markup will consist of two elements: a span used to present a play button, and the video’s thumbnail (optimised using the lazy loading technique for images covered above):

<span class="iframe_play" data-embed=""></span>
<img class="iframe_thumb" src="" />

Then, we can set it up so that the site waits until the visitor actually clicks on the video before loading the video itself. We do this by listening for mouse events on the video play button (the iframe_play element) and invoking a custom event handler.

$( document ).on( ‘click’, ‘.iframe_play’, play_video );

On this user action, you have two options: Swap the placeholder thumbnail for the video player itself, or create a lightbox overlay to display the video atop the page. Our play_video() function will employ the lightbox technique, automatically generating and launching a modal window that plays our video.

When the lightbox closes, it's removed from the page entirely, keeping the page’s markup clean and lightweight.

function close_video() {


Our tool dynamically generates the markup for both a modal overlay and a container for the video. In the case of YouTube, it's using an iFrame embed to build out the video element. Since the autoplay flag is set in the URL, the video will start immediately in the modal. Clicking on the close link will remove the generated markup, immediately stopping playback when the video is removed from the DOM.

function play_video( e ) {
    var $this = $( this ),
        video = this.getAttribute( ‘data-embed’ ),
        content = document.createElement( ‘iframe’ ),
        overlay = document.createElement( ‘div’ ),
        modal = document.createElement( ‘div’ ),
        closer = document.createElement( ‘a’ ),
        $overlay = $( overlay ), $modal = $( modal ), $closer = $( closer );
    content.src = video; = ‘position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: #000;z-index: 159900;’;
    $overlay.on( ‘click’, close_video ); = ‘position: fixed; top: 30px; left: 30px; right: 30px; bottom: 30px; background-color: #fff; z-index: 160000;’;
    closer.innerText = ‘close’;
    $closer.on( ‘click’, function( e ) { e.preventDefault(); close_video(); } );
    modal.appendChild( closer );
    modal.appendChild( content );
    document.body.appendChild( overlay );
    document.body.appendChild( modal );

Loading a video on-demand in a modal window provides all of the interaction of a standard embedded video. It just foregoes the overhead of loading the video’s media stream when the page first displays.

Further applications

Lazy loading web content allows a site to straddle the middle ground between providing a rich user experience and providing a speedy one. It's a clever use of engineering savvy to cut corners where possible without actually cutting down on the content displayed on the site. Thus far we’ve covered images and video, but just about any other content can be pulled in after the initial page load.

Sharing buttons and analytics

Sites like TechCrunch defer loading social media interactions (like Facebook, Twitter and LinkedIn buttons) until after the visitor hovers their mouse over the sharing region. With 20 articles on the homepage, this would usually mean loading 60 somewhat heavy remote includes. Waiting to load the social sharing icons until absolutely necessary is a considerable boost to site performance.

Other sites load analytics scripts, markup for standalone modules and even advertising asynchronously. Virtually any content that can be loaded separately from the page’s main markup and assets, should be.

The best of both worlds

Websites will continue to grow in scope and scale in order to present well-rounded experiences to those with capable hardware and seemingly-unlimited bandwidth. Presenting a similarly rich experience to those with limited access and less high-performing devices, though, doesn’t need to be a daunting task. Tricks like lazily loaded media help combine the best of both worlds.

Words: Eric Mann

Eric Mann is an expert in HTML, CSS, PHP and WordPress. Follow him on Twitter at @EricMann. This article originally appeared in net magazine issue 258.

Like this? Read these!

Have you good any great WordPress developer tips? Share experiences and ideas in the Comments!