Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you make images load lazily only when they are in the viewport?

I am seeing a lot of sites these days, mainly tutorial sites that have a lot of images and they only load images further down the page once they come into the view port?

How would I go about doing this? As you scroll down the page the images who were below the viewport fade in

like image 865
Colin Avatar asked Feb 23 '10 21:02

Colin


People also ask

How do you lazy load images?

Lazy Loading Techniques for images. Images on a webpage can be loaded in two ways - using the <img> tag, or using the CSS `background` property.

How do you lazy load an image in CSS?

We attach the observer on all the images to be lazy loaded. Once the API detects that the element has entered the viewport, using the isIntersecting property, we pick the URL from the data-src attribute and move it to the src attribute for the browser to trigger the image load.

How do you make a lazy load in HTML?

The loading attribute specifies whether a browser should load an image immediately or to defer loading of off-screen images until for example the user scrolls near them. Tip: Add loading="lazy" only to images which are positioned below the fold.


2 Answers

<img loading="lazy" does it without any Javascript

We are now getting more and more support for this standardized no-JavaScript method, which is very exciting!

  • https://caniuse.com/#feat=loading-lazy-attr
  • https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-loading

You can see this at work in the code snippet below.

To see that the loading is actually lazy, open Chrome DevTools in the Network tab.

Then, as you scroll down the snippet, you will see that the images only load when you see them.

I've also added an optional JavaScript button to show that you can change lazy back to the default eager from JavaScript, and images will then start to load immediately.

document.getElementById('load-now').addEventListener('click', function(){   for (const img of document.getElementsByTagName('img')) {     img.loading = 'eager';   } });
.separator {     height: 1000px;     width: 100px;     border: 5px solid red; } img {     height: 340px;     border: 5px solid black; }   #load-now {   border: 5px solid black;   }
<div id="load-now">Click me to load all images now!</div> <div><img loading="lazy" height="340" src="https://upload.wikimedia.org/wikipedia/commons/5/56/Donald_Trump_official_portrait.jpg"></div> <div class="separator"></div> <div><img loading="lazy" height="340" src="https://upload.wikimedia.org/wikipedia/commons/8/8d/President_Barack_Obama.jpg"></div> <div class="separator"></div> <div><img loading="lazy" height="340" src="https://upload.wikimedia.org/wikipedia/commons/d/d4/George-W-Bush.jpeg"></div> <div class="separator"></div> <div><img loading="lazy" height="340" src="https://upload.wikimedia.org/wikipedia/commons/d/d3/Bill_Clinton.jpg"></div> <div class="separator"></div> <div><img loading="lazy" height="340" src="https://upload.wikimedia.org/wikipedia/commons/9/90/George_H._W._Bush%2C_President_of_the_United_States%2C_1989_official_portrait_%28cropped%29.jpg"></div> <div class="separator"></div> <div><img loading="lazy" height="340" src="https://upload.wikimedia.org/wikipedia/commons/1/16/Official_Portrait_of_President_Reagan_1981.jpg"></div>

One really cool thing about this method is that it is fully SEO friendly, since the src= attribute contains the image source as usual, see also: Lazy image loading with semantic markup

Tested in Chromium Chromium 81 and Firefox 77.0.1, both worked and loaded lazily.

IntersectionObserver minimal runnable example

This is a JavaScript method that would work before img loading="lazy" was implemented.

This is essentially the technique used at: https://appelsiini.net/projects/lazyload/ which was mentioned at: https://stackoverflow.com/a/2322042/895245

Web APIs have evolved so much now that it is not hard to code it from scratch!

var observer = new IntersectionObserver(     (entries, observer) => {         entries.forEach(entry => {             if (entry.intersectionRatio > 0.0) {                 img = entry.target;                 if (!img.hasAttribute('src')) {                     alert('will load the image!!!');                     img.setAttribute('src', img.dataset.src);                 }             }         });     },     {} ) for (let img of document.getElementsByTagName('img')) {     observer.observe(img); }
.separator {     height: 1000px;     width: 100px;     border: 5px solid red; } img {     height: 340px;     border: 5px solid black; }
<div><img data-src="https://upload.wikimedia.org/wikipedia/commons/5/56/Donald_Trump_official_portrait.jpg"></div> <div class="separator"></div> <div><img data-src="https://upload.wikimedia.org/wikipedia/commons/8/8d/President_Barack_Obama.jpg"></div> <div class="separator"></div> <div><img data-src="https://upload.wikimedia.org/wikipedia/commons/d/d4/George-W-Bush.jpeg"></div> <div class="separator"></div> <div><img data-src="https://upload.wikimedia.org/wikipedia/commons/d/d3/Bill_Clinton.jpg"></div> <div class="separator"></div> <div><img data-src="https://upload.wikimedia.org/wikipedia/commons/9/90/George_H._W._Bush%2C_President_of_the_United_States%2C_1989_official_portrait_%28cropped%29.jpg"></div> <div class="separator"></div> <div><img data-src="https://upload.wikimedia.org/wikipedia/commons/1/16/Official_Portrait_of_President_Reagan_1981.jpg"></div>

Full page demo: https://cirosantilli.com/web-cheat/js-image-load-viewport.html

GitHub upstream: https://github.com/cirosantilli/cirosantilli.github.io/blob/1f637bf4791b115777300f48f427f0a6bb409fc1/web-cheat/js-image-load-viewport.html

This technique is just a combination of:

  • What is the best JavaScript code to create an img element
  • How can I tell if a DOM element is visible in the current viewport?

Tested in Chromium 76.

Change load order to nearest first

This is the last use case missing after loading="lazy" for me: a method that downloads eagerly, but changes the download order to download first on viewport, then below, and then above: Change loading order of images already on page

Maybe we can do something with querySelectorAll() to solve jQuery find next/prev elements of a certain class but not necessarily siblings and then remove loading=lazy from images in the JavaScript! This would both degrade gracefully, and be SEO friendly.

The last issue is how to get the first visible element though:

  • How to get the first DOM element that is visible in a viewport?
  • How to select the last element on viewport

I haven't seen a very good solution for this yet.

Lazy load video

Not sure why, but neither Chromium 81 nor Firefox 77.0.1 can lazy load video, now sure why did they do it just for img?

Chromium 81 did implement it for iframe however, which is what YouTube embeds use, while Firefox 77.0.1 didn't: lazy load iframe (delay src http call) with jquery


Simple solution that does not depend on JQuery:

    <script type="text/javascript">         refresh_handler = function(e) {         var elements = document.querySelectorAll("*[realsrc]");         for (var i = 0; i < elements.length; i++) {                 var boundingClientRect = elements[i].getBoundingClientRect();                 if (elements[i].hasAttribute("realsrc") && boundingClientRect.top < window.innerHeight) {                     elements[i].setAttribute("src", elements[i].getAttribute("realsrc"));                     elements[i].removeAttribute("realsrc");                 }             }         };          window.addEventListener('scroll', refresh_handler);         window.addEventListener('load', refresh_handler);         window.addEventListener('resize', refresh_handler);     </script> 
like image 39
Lenar Hoyt Avatar answered Sep 22 '22 00:09

Lenar Hoyt