Lazy-loading is a strategy to identify resources that are not critical for the initial page load, and load them only when needed. It's a way to shorten the length of the critical rendering path, which translates into reduced page load times. It leads to faster websites.
We have the ability to lazy-load images and iframes in browsers now via the loading
attribute.
<img src="cat.jpg" alt="felix the cat" loading="lazy">
<iframe src="video-player.html" title=".." loading="lazy"></iframe>
Support for lazy-loading is good. Lazy-loading of images is supported by all major browsers now. However, it is still a bit patchy for iframes. Firefox does not support lazy-loading of iframes, and it is currently under an experimental flag in Safari. So, we are gettting there!
It just made me wonder why the video
element has been overlooked. Is it because most videos live on YouTube now and are embedded on websites via iframes?
I don't know but I certainly hope not! 😕🤞
When writing an article recently, I had a short screen recording to demo some functionality. I wanted to include it as autoplaying video, similar to where you might use an animated GIF. That's when I realised there is no native lazy loading for videos.
This nudged me towards converting the video into a WebP instead. Did you know that WebP supports animation the same as a GIF but with better compression?
You can use an online video to WEBP converter for this task.
I didn't dig that deeply into this topic as it was a bit of tangent from what I was doing! However, I did read a recent enough article (late 2019) by Google Devs on this topic, aptly titled "Lazy-loading video". They describe 2 distinct use cases for embedding videos that are handled differently. Let's take a look at these to understand the topic better.
Use Case 1: Videos where playback is initiated by the user
You have controls on the video, and it up the user to play the video.
You can specify the preload
attribute on the video
element to control loading. By providing preload="none"
, the browser should be prevented from loading the video data.
<!-- disable preloading -->
<video controls preload="none" width="300" poster="img/cover.jpg">
<source src="files/sample.mp4" type="video/mp4">
</video>
Do not include the autoplay
attribute here as that may load the video, disregarding preload="none"
!
The browser default behaviors with regard to preload
are not set in stone, so being explicit with this is probably a good idea anyway.
On some browsers, the video will be have no background. You can make it look better by using the poster
attribute to show a preview image.
When the user clicks the play button of the video, then the video will be loaded.
Use Case 2: A video acting as an animated GIF replacement
This was my use case.
Google recommends using a lazy loading library such vanilla-lazyload, or you can write your own JavaScript code.
To write your own code is not too long.
In the HTML, you do not put src
attributes on the source
elements, instead you stash the video URL in the data-src
attribute. We mark video
with a "lazy" class.
<video class="lazy" autoplay muted loop playsinline width="600" height="300" poster="cover.jpg">
<source data-src="screen-recording.webm" type="video/webm">
<source data-src="screen-recording.mp4" type="video/mp4">
</video>
The JavaScript code uses the IntersectionObserver
API to detect when the video
element with the "lazy" class comes into view, and adds a src
attribute to each source
element with the video URL.
document.addEventListener("DOMContentLoaded", function() {
var lazyVideos = [].slice.call(document.querySelectorAll("video.lazy"));
if ("IntersectionObserver" in window) {
var lazyVideoObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(video) {
if (video.isIntersecting) {
for (var source in video.target.children) {
var videoSource = video.target.children[source];
if (typeof videoSource.tagName === "string" && videoSource.tagName === "SOURCE") {
videoSource.src = videoSource.dataset.src;
}
}
video.target.load();
video.target.classList.remove("lazy");
lazyVideoObserver.unobserve(video.target);
}
});
});
lazyVideos.forEach(function(lazyVideo) {
lazyVideoObserver.observe(lazyVideo);
});
}
});
Do you always want to do this for videos?
It looks to me like the addition of this functionality would be great. You could add preload="lazy"
; or theloading
attribute to match the other 2 elements.
Where is the suggestion box? 😄
Top comments (3)
Clearly, native support to lazy load iframes (especially YT's embed iframes) would be a big step forward. Video would just come next. I don't know why it's taking too much time though.
Autoplaying video... Ugh
Some comments have been hidden by the post's author - find out more