Menu Search
Jump to the content X X
Smashing Conf Barcelona

You know, we use ad-blockers as well. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. our upcoming SmashingConf Barcelona, dedicated to smart front-end techniques and design patterns.

Redefining Lazy Loading With Lazy Load XT

Lazy loading is a common software design pattern that defers the initialization of objects until they are needed. Lazy loading images started to become popular on the web back in 2007, when Mika Tuupola drew inspiration from the YUI ImageLoader utility and released a jQuery plugin1.

Since then, it’s become a popular technique to optimize page loading and the user experience. In this article I will discuss why we should and shouldn’t use Lazy Load, and how to implement it.

Further Reading on SmashingMag: Link

Why Lazy Load? Link

Images make up over 60% of an average page’s size, according to HTTP Archive6. Images on a web page would be rendered once they are available. Without lazy loading, this could lead to a lot of data traffic that is not immediately necessary (such as images outside of the viewport) and longer waiting times. The problem? Visitors are not patient at all. By lazy loading, images outside of the viewport are loaded only when they would be visible to the user, thus saving valuable data and time.

Lazy loading is not limited to images. It can be used on pages with complex JavaScript, iframes and third-party widgets, delaying the loading of these resources until the user actually needs them.

Why Not Lazy Load? Link

Lazy loading is not a silver bullet, and it is known to affect performance. For example, most lazy-loading implementations either don’t have a src attribute in the <img> tags (which is invalid syntax, according to the HTML5 standard) or point to a blank image (hello, spacer.gif). This approach requires duplicate <img> tags wrapped in <noscript> tags for browsers with JavaScript disabled (or with the NoScript plugin installed):

<img data-src="path" attributes /><noscript><img src="path" attributes /></noscript>

Fortunately, this duplication doesn’t increase the page’s size significantly when you enable Gzip compression. However, some search engines might not index your images correctly, because the <noscript> tag is not indexed within content, and the <img> tag outside of <noscript> is referring to a blank image. Currently, Google seems to eventually index lazy-loaded images, but other search engines are less likely to.

How Is Lazy Loading Implemented? Link

You might be overwhelmed by the number of lazy-load plugins out there. You might also think that implementing one is easy: Just monitor page scrolling (or resizing), and then set the src attribute when an image is visible. If only it were that easy. Many things come into play when building a solid solution that works on both desktop and mobile. So, how do you separate the signal from the noise?

  • Throttling
    Checking the visibility of images after every interaction (even a tiny bit of scrolling) could compromise the page’s responsiveness. To ease that, implement some sort of throttling mechanism.
  • All your mobile are belong to us
    There is no scroll event in the Opera Mini browser and some old feature phones. If you receive traffic from those devices, you should monitor and load all images directly.
  • Lazy load or automatic pagination?
    Some implementations check only whether an image is above the fold. If the page is scrolled down to the very bottom via an anchor (or the scrollTo method in JavaScript), then all images below the fold will begin to download, instead of only the images within the viewport. This is more a matter of automatic pagination because users will have to wait for the remaining images to load after an interaction.
  • Dynamic image insertion
    Many websites use AJAX navigation nowadays. This requires a lazy-load plugin to support the dynamic insertion of images. To prevent a memory leak, any references to images that are not in the DOM (for example, ones that appear after an AJAX-based replacement of content) should also be removed automatically.

This list is certainly not comprehensive. We have many more issues to consider, such as the lack of getBoundingClientRect in old browsers, a change in orientation without an ensuing resize event on the iPhone, or the particular handling requirements of the jQuery Mobile framework.

Unfortunately, most plugins do not handle all of the above.

Lazy Load XT Link

We’ve been optimizing web performance on numerous screens for almost a decade now. Our project Mobile Joomla7 has been applied to over a quarter billion web pages and is still one of the most popular ways to optimize Joomla websites for mobile. Thanks to this, we’ve been lucky to witness the evolution of the web from desktop to mobile and observe trends and changing needs.

With our latest project,, we’ve been working on an easy solution to automatically improve responsive design performance on all devices. Lazy loading became an integral part of the project, but we came to realize that current lazy-load implementations are insufficient for the growing needs of the modern web. After all, it’s not just about desktop, mobile and images anymore, but is more and more about other media as well, especially video (oh, and did I hear someone say “social media widgets”?).

We concluded that the modern web could use a mobile-oriented, fast, extensible and jQuery-based solution. That is why we developed one and called it Lazy Load XT348.

Here are its main principles, which consider both current and future applications:

  • It should support jQuery Mobile9 out of the box.
  • It should support the jQuery10, Zepto11 and DOMtastic12 libraries. Of course, writing the solution in native JavaScript is possible, but jQuery is a rather common JavaScript extension nowadays, and one of our aims was to simplify the transition from the original Lazy Load to Lazy Load XT. This makes jQuery an adequate choice. However, if you don’t want to use jQuery at all, read the “Requirements” section below for details on reducing the size of dependent libraries.
  • It must be easy to start. The default settings should work most of the time. Prepare the HTML, include the JavaScript, et voilà!

Include Link

Lazy Load XT requires jQuery 1.7+, Zepto 1.0+ or DOMtastic 0.7.2+. Including the plugin is easy and as expected:

<script src="jquery.min.js"></script>
<script src="jquery.lazyloadxt.min.js"></script>

<script>$.lazyLoadXT.extend({edgeY: 200});</script>

<style>img.lazy {display:none}</style>

Use Link

By default, the plugin processes all images on the page and obtains an image’s actual source path from the data-src attribute. So, the recommended snippet to place an image on the page is this:

<img class="lazy" data-src="path" [attributes] /><noscript><img src="path" [attributes] /></noscript>

From this snippet, it is clear why we’ve set img.lazy above to display: none: Hiding the image is necessary in case there is no JavaScript, or else both the original image and the placeholder would be displayed. If the src attribute of the <img> tag is not set, then the plugin will set it to be a transparent GIF using the data-uri attribute.

If you’re not worried about users who have disabled JavaScript (or about valid HTML5 code), then just load jquery.lazyloadxt.min.js and replace the src attribute in the images with data-src:

<script src="jquery.min.js"></script>
<script src="jquery.lazyloadxt.min.js"></script>
<img data-src="path" [attributes] />

Video Link

Lazy Load XT is available in two versions: jquery.lazyloadxt.js and jquery.lazyloadxt.extra.js. The latter includes better support of video elements, both <video> tags and ones embedded in <iframe> (such as YouTube and Vimeo).

Markup changes are similar to the above, and replacing the src attributes with data-src and post with data-poster is sufficient if you’re using them in a <video> element.

<script src="jquery.lazyloadxt.extra.js"></script>
<iframe data-src="//[videocode]?rel=0" width="320" height="240"></iframe>
<video data-poster="/path/to/poster.jpg" width="320" height="240" controls>
   <source data-src="/path/to/video.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'>
   <source data-src="/path/to/video.ogv" type='video/ogg; codecs="theora, vorbis"'>
<video data-src="/path/to/video2.mp4" width="320" height="240" controls>

Size Link

The size of the jquery.lazyloadxt.min.js file is 2.3 KB (or 1.3 KB Gzip’ed), and the size of jquery.lazyloadxt.extra.min.js is 2.7 KB (or 1.4 KB Gzip’ed). That’s small enough, especially compared to jQuery and Zepto.

Requirements Link

Even though Lazy Load XT requires jQuery, Zepto or DOMtastic, loading the full versions of any of them is not necessary. For example, DOMtastic requires only a minimal set of modules (attr, class, data, event, selector, type) for you to get a 7.9 KB file (or 2.7 KB Gzip’ed), bringing the total size of both DOMtastic and Lazy Load XT to just 4 KB (Gzip’ed).

Compatibility Link

We’ve tested Lazy Load XT in the following browsers:

  • Internet Explorer 6 – 11
  • Chrome 1 – 37
  • Firefox 1.5 – 32.0
  • Safari 3 – 7
  • Opera 10.6 – 24.0
  • iOS 5 – 7 (stock browsers)
  • Android 2.3 – 4.4 (stock browsers)
  • Amazon Kindle Fire 2 and HD 8.9 (stock browsers)
  • Opera Mini 7

Performance Link

We have tested Lazy Load XT’s performance on a page with one thousand images and are happy with the results: Scrolling works well even on old Android 2.3 devices.

We also successfully tested various iterations of Lazy Load XT on over one thousand websites for several months in our jQuery Mobile-based Elegance and Flat templates13.

Options Link

The plugin’s default settings may be modified with the $.lazyLoadXT object:

$.lazyLoadXT.edgeY = 200;
$.lazyLoadXT.srcAttr = 'data-src';

Note that you may change this object at any time: before loading the plugin, between loading and when the document is ready, and after the event is ready. (Note that the last option doesn’t affect initialized images.)

Lazy Load XT supports a lot of options and events, enabling you to integrate other plugins or implement new features. For the full list and details, see Lazy Load XT’s GitHub page14.

AJAX Support Link

If you use jQuery Mobile with built-in AJAX page loading, then the Lazy Load XT plugin will do all of the magic for you in the pageshow event. In general, you should run the code below to initialize images inside a container with AJAX-loaded content.


Or run this:


Extending Lazy Load XT Link

Lazy Load XT can be extended easily using the oninit, onshow, onload and onerror handlers or the related lazyinit, lazyshow, lazyload and lazyerror events. In this way, you can create amazing add-ons.

Some examples can be found on the GitHub page15, along with usage instructions16. We’ll highlight just a few of them here.

Loading Animation Link

Customizing the image-loading animation is easy. By default, Lazy Load XT includes spinner17 and fade-in18 animations, but you can use any effects from the Animate.css19 project or any other.

Responsive Images Link

Lazy Load XT has two add-ons for responsive images20. One is “srcset,” to polyfill the srcset attribute (and that should be renamed data-srcset):

<img data-srcset="image-hd.jpg 2x, image-phone.jpg 360w, image-phone-hd.jpg 360w 2x">

The second is “picture,” a polyfill for the <picture> tag:

<picture width="640" height="480">
   <br data-src="small320.jpg">
   <br media="(min-width: 321px)" data-src="medium480.jpg">
   <br media="(min-width: 481px)" data-src="large640.jpg">
   <noscript><img src="large640.jpg"></noscript>
   <p>Image caption</p>

Page Widgets Link

Lazy Load XT makes it possible to lazy-load page widgets21 (such as Facebook, Twitter or whatever widget you like). Insert any HTML code in the page using the “widget” add-on when an element becomes visible. Wrap the code in an HTML comment inside of a <div> with an ID attribute, and give the element a data-lazy-widget attribute with the value of that ID:

<!-- Google +1 Button -->
<div data-lazy-widget="gplus" class="g-plusone" data-annotation="inline" data-width="300"></div>
<div id="gplus"> <!--
   (function() {
      var po = document.createElement('script'),
      s = document.getElementsByTagName('script')[0];
      po.type = 'text/javascript'; po.async = true;
      po.src = '';
      s.parentNode.insertBefore(po, s);

If the data-lazy-widget attribute has an empty value, then the element itself will be used as a wrapper:

<div data-lazy-widget><!--

Many other add-ons are available, too. They include infinite scrolling, support for background images, loading all images before displaying them (if the browser supports it), and deferring the autoloading of all images.

Is There A Silver Bullet? Link

Lazy loading images is not a standard browser feature today. Also, no third-party browser extensions exist for such functionality.

One might assume that the lazyload attribute in the “Resource Priorities22” draft specification by Microsoft and Google would do it. However, it has another purpose: to set the background priority for a corresponding resource element (image, video, script, etc.). Thus, if your aim is to load JavaScript or CSS before images, that’s your choice. There is another killer attribute, postpone, which prevents any resource from loading until you set the CSS display property to a value other than none. The good news is that support for the lazyload attribute is in Internet Explorer 11. The bad news is that the postpone attribute has not been implemented yet.

We do not know when or if the draft specification above will ever be fully supported by the major browsers. So, let’s look at the solutions we have now.

Some people have attempted to solve the duplication of the <img> tag in <noscript> tags by keeping only the <noscript> part and processing it with JavaScript. Unfortunately, <noscript> has no content in Internet Explorer, and it is not included in the DOM at all in Android’s stock browser (other browsers may behave similarly).

An alternative would be to use the <script> tag, instead of <noscript>, like so:

<script>function Z(){document.write('<br ');}</script>
<script>Z();</script><img src="path" attributes />

So, <img> would be an attribute of the <br> tag and would transform <br> tags into <img data-src> at the document.ready event. But this method requires document.write and is not compatible with AJAX-based navigation. We have implemented this method in the script add-on for Lazy Load XT, but the standard way using data-attributes seems to be clearer.

Finally, Mobify has an elegant Capturing API23 (see the recent review on Smashing Magazine24) that transforms HTML into plain text using the following code and then processes it with JavaScript:

document.write('<plaintext style="display:none">');

Unfortunately, this solution has drawbacks of its own: It is quite slow, and the browser might treat it as a JavaScript-based HTML parser. Also, combining this solution with AJAX navigation is not clear, and it is not guaranteed to work correctly in all browsers because the <plaintext> tag was deprecated in HTML 2. It actually doesn’t work in W3C’s Amaya browser and on some feature phones (such as Nokia E70). Nevertheless, these are edge cases, and you may use Mobify.js and Lazy Load XT simultaneously, although that is beyond the scope of this article.

Comparing Lazy Load Solutions Link

Both Lazy Load XT and the original Lazy Load are not the only solutions around. Below we compare most of the major existing solutions:

FeatureLazyLoad for jQuery25Lazy Load XT3326Unveil27Lazy28 (by Eisbehr)Responsive Lazy Loader29bLazy30Lazyload31 (by VVO)Echo32

Current version 1.9.3 1.0.5 1.3.0 0.3.7 0.1.7 1.2.2 2.1.3 1.5.0
Dependencies jQuery jQuery, Zepto or DOMtastic jQuery or Zepto jQuery jQuery
Size (Gzip’ed) 1.19 KB 1.31 KB (or 1.45 KB with extras) 338 B 1.45 B 1.23 KB 1.24 KB 1.01 KB 481 B
Skips images above the fold yes yes yes no yes yes no yes
Loading effects yes yes yes (with custom code) yes yes (with custom code) yes (with custom code) no no
Responsive images no yes (via plugin) yes no yes yes yes (with custom code) no
Supports scroll containers yes yes no yes yes no yes no
Supports horizontal scrolling yes yes no no yes yes yes yes
Throttling no yes no yes no yes yes yes
Lazy background images yes yes (via plugin) no yes no no no no
Lazy <video> tag no yes no no no no no no
Lazy iframes no yes no no no no no no
Supports Opera Mini no yes no no no no no no

Conclusion Link

The total size of media elements on the average web page is increasing constantly. Yet, especially on mobile devices, performance bottlenecks remain, which stem from bandwidth issues, widely varying network latency, and limitations on memory and the CPU. We need solutions for better and faster browsing experiences that work across all devices and browsers.

While no single lazy-load standard exists so far, we welcome you to try Lazy Load XT, especially if lazy-loaded video or other media is an important part of your website’s functionality.

Download and Contribute Link

Bug reports, patches and feature requests are welcome.

(al, ml)

Footnotes Link

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14”
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37

↑ Back to top Tweet itShare on Facebook

Denis Ryabov is lead developer of Mobile Joomla!, the most popular way to display Joomla! websites on mobile, as well as He is also a physicist with PhD from Southern Federal University.

Ugur Kaner is product designer, serial entrepreneur and startup advisor with computer engineering BSc. He’s specialized in UX, mobile and connected devices. Over the past 10 years he’s built technology startups and award winning products, besides helping global brands i.e Udemy, Rovio, Adobe, Nokia & Al Jazeera.

  1. 1

    Thanks a lot for supporting DOMtastic! Just a small note: the command to create a custom build now looks like this: “bin/custom –include=attr,class,data,event,selector,type”.

    • 2

      Initial version of the article was written a year ago, and at that time `gulp` script was used. And thank you for DOMtastic!

  2. 3

    Nice. Thanks for the heads up.

    One thought, and perhaps I’m over-thinking it, but a fixed edgeY works but maybe there’s a better way? For example, a larger image might need more lead time (so to speak), yes?

    • 4

      Lazy Load XT supports per-image edgeY parameter, so this way is possible, but it’s necessary to implement a custom method to add images to LLXT’s list.

  3. 5

    Hey there, very well written article. I can’t help but critique the decision to make your library dependable on any JS framework. Native code would have been better, even if harder to implement. Example: I would avoid your code because of its dependencies alone ignoring however good it may be. Especially in the mobile world where every KB counts.

    Next time go vanilla :)

  4. 6

    For the image size do you need to have a width and height? My site is responsive so will adding dimensions to the image inline be an issue?

    • 7

      Width/height attributes are necessary to correctly reserve place for image. In the case of “standard” responsive style img{max-width:100%}, width of image is detected correctly (equal to the min of width attribute and width of parent element), but height is not: it will be equal to the value of height attribute or to default 50px (browser-depended) in the case of “standard” img{height:auto}. But anyway it is better than keep all images with default 50×50 size (or even 1×1, taking into account that data-uri used in Lazy Load XT is 1×1 transparent image).

      If you would like to have correct placeholders for not loaded images, most likely it’s possible to do with javascript, just iterate over all “hidden” images and set their height to be 0px and padding-bottom to be (100*height/width)+’%’. I wouldn’t like to include it into Lazy Load XT, but it is really quite easy to do using oninit and onload events.

  5. 8

    Jeffrey Bennett

    February 4, 2015 8:29 pm

    Invalid syntax where it says:
    <noscript><img data-src="large640.jpg"></noscript>

  6. 11

    Nice to see the jQuery Lazyloader being taken to the next level.

    Is there some performance comparison with the other lazyloader ( maybe) and some data on paint / reflow ?
    The later two are the main performance issue when it comes to lazyloading.

  7. 12

    You could speed up the this.each() loop ( with a while-pop construct (see ). That one beats native + jquery each(), underscore and lowdash

  8. 13

    Kai / Echt Einfach TV

    April 19, 2015 6:03 am

    For a while I am concerned with all our well-designed maths images *not indexed* by google or other search engines. We used this syntax specifying a dummy in the SRC attribute:

    I was searching for a solution, you gave it to me: The noscript tag! Did not think of this wonderful workaround.

    I am sure this solution is much better for now.

    Let us hope that all browsers start to implement a lazy load mechanism by themselves. So all the tweaks would not be necessary anymore.

    Kai from

  9. 14

    Peter Linzenkirchenr

    June 1, 2015 11:05 pm

    many thanks for this fine plugin!
    I have one question: i want to switch background images from landscape to portrait when orientation is switched. I edited the bg extension to handle two data attributes (data-bg and data-bg-portrait) but in order to get the effect i want (reloading the correct image when orientation is changed) i had to reset Lazy Load XT – or destroy and re-init it. Is there a way to accomplish this? In the moment i force a reload of the page which is not very convenient for the users.
    Many Thanks!

  10. 15

    Recently I explored a new way of implementing lazy loading using the srcset attribute. The html code is valid, absolutely no unnecessary request, works with responsive images. You can check it out at and


↑ Back to top