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
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.
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.
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.
<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!
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:
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:
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With