37

I am creating a page which will contain a lot of large sized images, so naturally I want to make sure the page loads without too much trouble. I read this article here http://24ways.org/2010/speed-up-your-site-with-delayed-content

The method of deferring is as follows (pulled from page, don't mind the URL)

<div>
    <h4>
        <a href="http://allinthehead.com/" data-gravatar-hash="13734b0cb20708f79e730809c29c3c48">
            Drew McLellan
        </a>
    </h4>
</div>

then later a snippet of js takes care of the image loading

$(window).load(function() {
    $('a[data-gravatar-hash]').prepend(function(index){
        var hash = $(this).attr('data-gravatar-hash')
        return '<img width="100" height="100" alt="" src="http://www.gravatar.com/avatar.php?size=100&amp;gravatar_id=' + hash + '">'
    });
});

I don't plan on doing this for every image but definitely for some image which I don't need it to show up at page load time.

Is this the best way to go or are there better ways to achieve faster page load by deferring images?

Thanks

Huangism
  • 15,324
  • 5
  • 45
  • 64
  • For anyone who's looking how to do it with css: background-image, here's the answer:https://stackoverflow.com/questions/39664660/how-to-defer-background-images-without-jquery-or-lazy-loading – Mario Nezmah Feb 03 '21 at 14:17

5 Answers5

62

A little late, but in case it benefits others, there is a great article on this topic by Patrick Sexton https://varvy.com/pagespeed/defer-images.html

He basically is suggesting the same thing, only by using tiny base 64 encoded images, he can place his image tags directly in the HTML which has the benefit of being able to control attributes like height, width, alt, etc individually. It will be a lot easier to maintain your HTML this way as opposed to creating the entire image tag in a script.

<img src="data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" data-src="image1.jpg" alt="image 1">
<img src="data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" data-src="image2.jpg" alt="image 2">

Then your script is simple and generic for all images

<script>
function init() {
  var imgDefer = document.getElementsByTagName('img');
  for (var i = 0; i < imgDefer.length; i++) {
    if (imgDefer[i].getAttribute('data-src')) {
      imgDefer[i].setAttribute('src',imgDefer[i].getAttribute('data-src'));
    }
  }
}

window.onload = init;
</script>
Sgnl
  • 1,632
  • 19
  • 29
jsolis
  • 768
  • 6
  • 9
  • How can this be the accepted answer ? It defeats the purpose of it all : if your image data is already base64-encoded in the HTML you don't defer image loading and you load *all* images with the HTML. You will have the visual effect of the images appearing but no deferred loading and no improvement in loading time. – MindTailor Dec 14 '18 at 09:21
  • 16
    The base64 encoded image isn't the real image. It's a super small blank dummy image. You'll notice the data is the same for the 2 example img tags. The script provided will lazy load the real image based on the data attribute. The article linked to the answer provides a very easy to understand explanation. – jsolis Dec 14 '18 at 19:09
  • 1
    OK, my bad. Thanks for the further explanation :) – MindTailor Dec 17 '18 at 17:37
  • I wish there was an easier way, it's fine to do this when you own all the code, but when using 3rd party libraries such as a carousel, etc. – Vadorequest Dec 25 '18 at 19:27
  • 4
    What advantage does the 1x1 pixel image have over just omitting the `src` attribute? – David Harkness Feb 13 '19 at 20:12
  • 1
    That is a great question. My best guess is that at the time (4 years ago now) it helped with the browser honoring height and width. Today when I test with and without the src attribute, I seem to get the same results in Chrome, FF, and Safari: https://codepen.io/anon/pen/qgMLyx – jsolis Feb 14 '19 at 15:47
5

This seems to be pretty clean way of deferring images. The only potential problem is if images carry important information as "Data attributes are a new feature in HTML5".

Another option could be to put images to end of body and use CSS to position them. Personally I would stick to javascript.

nrodic
  • 2,953
  • 3
  • 30
  • 35
2

Here's a version showcasing .querySelectorAll:

function swapSrcAttributes(source) {
  return function(element) {
    element.setAttribute('src', element.getAttribute(source));
  }
}

function forEach(collection, partial) {
  for (var i = 0; i < collection.length; i++) {
     partial(collection[i]);
  }
}

function initDeferImages() {
  // for images
  var deferImages = document.querySelectorAll('img[data-src]');

  // or you could be less specific and remove the `img`
  deferImages = document.querySelectorAll('[data-src]');

  forEach(deferImages, swapSrcAttributes('data-src'));
}

window.onload = function() {
  initDeferImages();
}

Here is the compatibility table for .querySelector and .querySelectorAll via https://caniuse.com/#feat=queryselector

Sgnl
  • 1,632
  • 19
  • 29
0

Html

<img
    width="1024"
    src="https://placehold.it/64x48.jpg"
    data-src-defer="https://images.unsplash.com/photo-1570280406792-bf58b7c59247?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1486&q=80"
    alt="image 1"
/>
            
<img
    width="1024"
    src="https://placehold.it/64x48.jpg"
    data-src-defer="https://images.unsplash.com/photo-1557053964-d42e8e29cb27?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=80"
    alt="image 2"
/>

JS

function deferImgs() {
    Array
    .from(document.querySelectorAll("img[data-src-defer]"))
    .forEach((element) => {
        element.setAttribute("src", element.dataset.srcDefer);
        });
}
    
window.addEventListener("load", deferImgs());

================================================================

I'm trying to comply with Farrukh's request with this edit.

I try to do my best, but English is unfortunately only the third language I speak. And I am not a language genius. :D

This js code snippet illustrates a delayed load of some big pictures. This is not a practical implementation. The size difference between the images is intentionally huge. This is because the test must be illustrative. You can monitor its operation through a browser development tool page. F12 > Network tab > Speed settings dropdown The ideal network speed for the test is between 1 - 3MB/s (Some slow network speed). You may want to run the test several times, so you can see, that the order in which the images are loaded is not controlled in this case, but depends on the transmission. Because it is not regulated, it is not possible to predict, which image will arrive first.

We load first a small image into a large placeholder. (image: 64x48.jpg > placeholder width="1024").

The querySelectorAll() method returns a static nodelist. This list of nodes at first glance looks like an array, but it's not.

This is an array-like object:

document.querySelectorAll("img[data-src-defer]")

The Array.from() method can creates a new array instance from this object. The forEach method can now be executed on this array. The forEach() method executes a provided function once for each element of the array.

In this case, each element of the array is passed once to this function:

(element) => {
        element.setAttribute("src", element.dataset.srcDefer);
        }

and this function sets the value of the src="" attribute of the image tag, to the value of the dataset of the same image tag.

src="https://placehold.it/64x48.jpg";

data-src-defer="https://images.unsplash.com/photo-1570280406792-bf58b7c59247?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1486&q=80";

src = data-src-defer;

So finally:

src="https://images.unsplash.com/photo-1570280406792-bf58b7c59247?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1486&q=80";
0

You can do it as simple as the example below:

All images have data-src attribute where you put the file path. And src attribute with a fake transparent 1x1px png image. You can also add loading attribute setted to lazy, it tells modern browsers to avoid to load immediately the images that are out of viewport (visible site zone)

<img data-src="path/to/image.jpg" loading="lazy"
     src="data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" />

Add this script to make all images get the src attribute once your site is loaded (you need jQuery to make it work)

$(function(){
    $("img[data-src]").attr("src", function(){ return $(this).data("src"); });
});
Alex Logvin
  • 426
  • 4
  • 8