50

If I have an image tag like the following:

<img src="myimage.jpg" />

and if I add "async" to it:

<img async src="myimage.jpg" />

will the image load asynchronous?

amateur
  • 43,371
  • 65
  • 192
  • 320
  • `````` Read more about ```loading``` attribute at MDN Web Docs: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Img – Zaheer May 04 '23 at 14:46

11 Answers11

49

The way to async load (lazy load) the content is to not set the 'src' attribute and then execute a script that loads the images once DOM-ready is launched.

 <img data-lazysrc='http://www.amazingjokes.com/images/20140902-amazingjokes-title.png'/>

and with jQuery (or possible with plain JavaScript too) use below code (as suggested here):

<script>  
function ReLoadImages(){
    $('img[data-lazysrc]').each( function(){
        //* set the img src from data-src
        $( this ).attr( 'src', $( this ).attr( 'data-lazysrc' ) );
        }
    );
}

document.addEventListener('readystatechange', event => {
    if (event.target.readyState === "interactive") {  //or at "complete" if you want it to execute in the most last state of window.
        ReLoadImages();
    }
});
</script>
patrick
  • 11,519
  • 8
  • 71
  • 80
  • 7
    why does this get a downvote without a comment? Code works like a charm and gives to OP the solution on how he could lazy-load his content... – patrick Feb 15 '17 at 17:00
  • window load is not dom ready. `jQuery(function($) { ... })` will load the images sooner. – B2K Apr 10 '18 at 14:32
  • @B2K, downside of that is that DOM-read gets loaded launched later and the user still has to wait while the images are loaded, which would be one of the reasons to use lazy loading... using ` – patrick Apr 10 '18 at 19:40
  • I was objecting to your script which uses window load instead of dom-ready. Dom ready is `$(document).ready(function() { ... })` not `$(window).load(function() { ... })`. Window load is fired later than Dom ready. See https://stackoverflow.com/questions/8396407/jquery-what-are-differences-between-document-ready-and-window-load for details. – B2K May 14 '18 at 15:45
  • I know what lazy loading is. I'm referring to your use of Dom-ready in your first paragraph and then window load in your example. That's inconsistent. I'm asking you to clarify your answer. – B2K May 15 '18 at 18:43
  • This does work however it is not W3C compliant as img tags should always have a non empty src attribute – Borjante Jun 21 '18 at 07:40
  • Don’t just point out the obvious, please also provide the solution, which isn’t too hard. Either add a ‘data:’ source or add a src to a small image that says ‘lazy loading’ (elegant solution too) or something... – patrick Jun 22 '18 at 19:42
  • 1
    shouldn't it be **lazysrc** instead of **lazyload** ?? – Myoch Jun 05 '19 at 07:26
  • 2
    This will work like `defer`, not `async`. There is a big difference between both attribute. – Bhavik Hirani May 14 '20 at 09:19
33
var img = new Image(),
    url = "myimg.jpg",
    container = document.getElementById("holder-div");

img.onload = function () { container.appendChild(img); };
img.src = url;

This would start loading an image as soon as you request it in-script, and whenever the image was done loading, it would grab and add the image to it.

There are lots of other ways of doing this...
This is just a dead-simple example of async loading of a single image.

But the moral is this:
For async loading to work, either load it in JavaScript and use the onload, or include the image tag on the page, without the src attribute (specify the width and height in HTML), and go back at some point, in JS, and set the image URL.

Norguard
  • 26,167
  • 5
  • 41
  • 49
  • 1
    Following this I made `` Before mostly css/js. It only loads the image file one time. Then one can set the image normally, as bg via css or otherwise. Rarely it appears loading/partly, but sometimes does. – Iacchus May 05 '15 at 05:35
  • 1
    @iacchus `async` attribute will not work for inline `script` tags (without `src`). – Artur Aleksanyan Jul 07 '17 at 10:06
31

The modern way to do this is with the loading attribute for images and iframes.

Attribute: loading=lazy

This will defer loading of the content until the element reaches a calculated distance from the viewport (that just means, it's got quite likely that the user will scroll it into view).

<img src="defer.png" loading="lazy" alt="An Awesome Image" width="500" height="400">

Setting the attribute to lazy invokes the new behaviour.

This is already in Chromium since v76, but might not hit non-Chromium browsers until it goes through the usual specification shennanigans.

If you are going to defer loading using a script, it would be worth writing the image with the lazy attribute and polyfilling the behavior as opposed to working off of a class name, etc. That way, you can allow the native version to take over as it becomes available.

Forced Eager Loading

Automatic lazy loading may become a feature of lightweight browsing, in which case, you may want to do the inverse and force an image to load. You can use the same loading attribute with a value of eager to ask the browser to grab the image even if it might otherwise choose not to.

<img src="defer.png" loading="eager" alt="An Awesome Image" width="500" height="400">

Further reading

View the pull request for the WHATWG spec

Fallback JavaScript with notes about perhaps not using fallbacks

Fenton
  • 241,084
  • 71
  • 387
  • 401
11

An alternate way to async load an image is by using Promise in javascript, which serves the purpose of doing things asynchronously.

function asyncImageLoader(url){
    return new Promise( (resolve, reject) => {
        var image = new Image()
        image.src = url
        image.onload = () => resolve(image)
        image.onerror = () => reject(new Error('could not load image'))
    })
}    

// then use it like this

var image = asyncImageLoader(url)


image.then( res => {
    console.log(res)
})
  
anekix
  • 2,393
  • 2
  • 30
  • 57
  • The [vanillaJS](http://vanilla-js.com/) library you suggest is popular, but has had some hit-and-miss browser support over the years, so a lot of folks won't rely on it. 5 years ago, I would have recommended using jQuery instead, without irony. – jpaugh Jul 01 '20 at 19:49
  • 1
    @jpaugh I wanted to add an updated answer to the mentioned alternatives. I have edited the wording in answer to reflect that :) – anekix Jul 01 '20 at 21:54
8
<img async src="myimage.jpg" />

The image tag doesnt supports any async attribute.

http://www.w3.org/TR/html5/embedded-content-0.html#the-img-element

pwdst
  • 13,909
  • 3
  • 34
  • 50
mpm
  • 20,148
  • 7
  • 50
  • 55
6

While several other answers highlight ways to fetch images asynchronously, it may also be helpful to know that the <img /> tag supports an attribute that serves as a hint to the browser that may result in images being be decoded asynchronously. It doesn't appear to be supported by Internet Explorer.

<img src="myimage.jpg" decoding="async"/>

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Img#attr-decoding

https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decoding

https://github.com/whatwg/html/issues/1920

derekbaker783
  • 8,109
  • 4
  • 36
  • 50
3

If you're using jQuery, I did something simple, yet effective, like this:

HTML

<div data-lazy-load-image="/Images/image-name.png" data-image-classname="pop-in"></div>

JavaScript

$(function () {
    $("[data-lazy-load-image]").each(function (index, element) {
        var img = new Image();
        img.src = $(element).data("lazy-load-image");
        if (typeof $(element).data("image-classname" !== "undefined"))
            img.className = $(element).data("image-classname");
        $(element).append(img);
    });
});

CSS

@-webkit-keyframes pop-in {
    0% { opacity: 0; -webkit-transform: scale(0.5); }
    100% { opacity: 1; -webkit-transform: scale(1); }
}
@-moz-keyframes pop-in {
    0% { opacity: 0; -moz-transform: scale(0.5); }
    100% { opacity: 1; -moz-transform: scale(1); }
}
@keyframes pop-in {
    0% { opacity: 0; transform: scale(0.5); }
    100% { opacity: 1; transform: scale(1); }
}

You could extend this to include additional optional attributes for each image, but you get the idea.

This will wait until the DOM is ready, then dynamically (async) load the images into the element that you mark with the data-lazy-load-image attribute. I included the CSS to make the images "pop in" when they are loaded.

John Washam
  • 4,073
  • 4
  • 32
  • 43
  • Would appreciate if you downvote that you also comment, so I can know how you think the answer needs improved or fixed. – John Washam Apr 14 '20 at 14:57
2

While @Norguard's example is quite simple and easy enought for an image or two, I have found echo.js pretty handy for lazy-loading, https://github.com/toddmotto/echo.

It does lazy-loading images with data-* attributes and comes with some neat other things too.

<img data-echo="img/photo.jpg">
<script src="dist/echo.js"></script>
<script>
    echo.init();
</script>
Victor Häggqvist
  • 4,484
  • 3
  • 27
  • 35
1

You can read more about lazyload attribute:

<img src="myimage.jpg" alt="some description" lazyload/> - with default values

or you can prioritize:

<img src="myimage.jpg" alt="some description" lazyload="1"/>
<img src="myimage.jpg" alt="some description" lazyload="2"/>
  • 2
    "This specification is no longer maintained and has been abandoned." http://www.w3.org/TR/resource-priorities/ – Don McCurdy Oct 21 '15 at 16:30
1

I have used the following approach with jQuery.

First, don't use a "src" attribute in the image tag, but put your source into a different attribute, like this:

<img async-src="/mydirectory/myimage.jpg" />

Then, within the jQuery document-ready function, I use this code to copy the element's async-src to the element's actual src:

$("img[async-src]").each(function(index) {
    $(this).attr("src", $(this).attr("async-src"));
});

Notes:

jQuery's .each function may process the tags in the sequence they are coded in the HTML/DOM, but image sizes and network issues may mean that images don't actually load sequentially. In other words, your third async-src image might visually appear onscreen before the first has finished loading.

If your page layout relies on the pixel dimensions of that image file — e.g. you're not defining the image's dimensions via tag attributes, CSS, or a parent element — then you may have to use a "src" attribute on the original file pointing to a blank white or clear GIF of the dimensions you want.

Finally, if you want to process some code after the async loading of the image — for example, to handle a fading effect or change a CSS tag relevant to the element — expand the jQuery like this:

$("img[async-src]").each(function(index) {
    $(this).load(function() {
        // code to run after loading
    });
    $(this).attr("src", $(this).attr("async-src"));
});
Deke
  • 143
  • 1
  • 4
1

It might be too late of an answer but recently was facing the same issue and the "lighthouse" in the console suggested that I should follow what's mentioned here in the link: enter link description here

Basically, I did the following as suggested and it works really well:

  <script src="lazysizes.min.js" async></script>
  <!-- Images End -->
</body>

You may download the lazysizes.min.js from https://raw.githubusercontent.com/aFarkas/lazysizes/gh-pages/lazysizes.min.js

and source it locally.

Then, add the class lazyload to images that should be lazy loaded. In addition, change the src attribute to data-src.

For example:

<img data-src="images/flower3.png" class="lazyload" alt="">

You may be wondering why it is necessary to change the src attribute to data-src. If this attribute is not changed, all the images will load immediately instead of being lazy-loaded. data-src is not an attribute that the browser recognizes, so when it encounters an image tag with this attribute, it doesn't load the image. In this case, that is a good thing, because it then allows the lazysizes script to decide when the image should be loaded, rather than the browser.

Visit the reference for better understanding.

Hopefully it'll be of help to someone :)

Brajinder Singh
  • 159
  • 3
  • 8