preface
Lazy loading picture almost rotten street “is actually a word, in large and small, the interview will be frequently asked, I have also been asked in the previous interview to picture the cause of the lazy loading, implementation approach and the underlying principle, but because he seldom do” pictures “the processing of related, for” lazy loading “is poorly understood, So the question and answer in the interview is not very good.
Today, I will first from the browser bottom rendering mechanism to analyze why to do lazy loading pictures, then I will take you to look at the current mainstream of several lazy loading pictures and their implementation principle, and finally will make a prospect.
Why do lazy loading of images
To answer this question, let’s take a look at the browser’s underlying rendering mechanism:
Build a DOM tree
2. Style calculation
3. Layout stage
4, layering,
5, drawing
6, block
7. Rasterization
8, synthetic
If img is encountered during DOM construction, it will behave differently in the old and new versions of Chrome:
- Old version: Block DOM rendering
- New: While it does not block DOM rendering, each image request consumes one HTTP, and Chrome allows up to six SIMULTANEOUS TCP connections to the same Host
When you open a web site, the browser will do a lot of work, including the download may use a variety of resources, and then apply colours to a drawing is presented in front of you, suppose you have a lot of pictures, then the loading process is very time-consuming, especially as the electricity class needs a large number of image of the site, it is conceivable that the website of the initial loading time will be very long, Add in other influences, such as the Internet, and the user experience will be poor.
You’ve often come across a website that’s stuck somewhere and keeps loading, and it’s not a good experience. We all want to type in the url and have the page appear immediately.
To summarize: loading all of them directly slows down rendering, produces a white screen, and so on, affecting the user experience.
Image lazy loading based on native JS
The relevant API
Let’s start with a couple of apis that we’ll use later
document.documentElement.clientHeight
Gets the height of the visible area of the screen.
Image credit: MDN
element.offsetTop
Gets the height of the element relative to the top of the document.
Photo by Ruan Yifeng blog
document.documentElement.scrollTop
Gets the distance between the top of the browser window and the top of the document, which is how far the scroll bar scrolls.
Photo credit: Seven’s Blog
Thought analysis
Using the above three apis, we get three values: the height of the visible area, the distance of the element from the top of its parent element container, and the distance between the top of the browser window and the top of the container element, which is the height of the scroll bar.
Even though these guysAPI
It is very simple, but it is still a little abstract. Here we still use the figure to show it:
If offsetTop-scroolTop
Code implementation
Based on the above analysis, we could easily write the following code:
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Image lazy loading based on native JS</title>
<style>
img {
display: block;
width: 100%;
height: 300px;
margin-bottom: 20px;
}
</style>
</head>
<body>
<img data-src="./img/1.png" alt="">
<img data-src="./img/2.png" alt="">
<img data-src="./img/3.png" alt="">
<img data-src="./img/4.png" alt="">
<img data-src="./img/5.png" alt="">
<img data-src="./img/6.png" alt="">
<img data-src="./img/7.png" alt="">
<img data-src="./img/8.png" alt="">
</body>
<script>
var imgs = document.querySelectorAll('img');
//offsetTop is the distance between the element and offsetParent, looped to the top of the page
function getRealTop(e) {
var realTop = e.offsetTop;
while(e = e.offsetParent) {
realTop += e.offsetTop;
}
return realTop;
}
function lazyLoad(imgs) {
var H = document.documentElement.clientHeight;// Get the height of the visible area
var S = document.documentElement.scrollTop || document.body.scrollTop;
for (var i = 0; i < imgs.length; i++) {
if (H + S > getRealTop(imgs[i])) {
imgs[i].src = imgs[i].getAttribute('data-src'); }}}window.onload = window.onscroll = function () { //onscroll() is triggered when the scroll bar is scrolling
lazyLoad(imgs);
}
</script>
</html>
Copy the code
But if you print the code above in lazyLoad, you’ll see that lazyLoad gets called a lot as the scroll bar goes up and down, causing a lot of performance damage, so we can throttle events here
Image lazy loading based on getBoundingClientRect()
Let’s take a look at the API:
GetBoundingClientRect () is used to get the left, top, right, and bottom positions of an element on the page relative to the browser window. GetBoundingClientRect () is the distance from the DOM element to the viewable range of the browser (excluding the invisible parts of the page).
This function returns a rectObject object with six attributes: top, left, bottom, right, width, height; Width and height are the width and height of the element itself, but right and bottom are a little different. Right is the distance from the right edge of the element to the left of the window, and bottom is the distance from the bottom edge of the element to the top of the window.
Thought analysis
With this API, we can easily get rectobject. top, the vertex position of the IMG element relative to the viewport, as long as this value is less than the browser height window.innerheight to indicate that the viewport is in:
function isInSight(el){
const bound = el.getBoundingClientRect();
const clientHeight = window.innerHeight;
return bound.top <= clientHeight;
}
Copy the code
Code implementation
Here combined with the first implementation, make a transformation, and get:
function loadImg(el){
if(! el.src){const source = el.getAttribute('data-src');; el.src = source; }}function checkImgs(){
const imgs = document.querySelectorAll('img');
Array.from(imgs).forEach(el= >{
if(isInSight(el)){ loadImg(el); }})}window.onload = function(){
checkImgs();
}
document.onscroll = function () {
checkImgs();
}
Copy the code
Lazy image loading based on IntersectionObserver
concept
Again, let’s look at the concept.
Here we refer to The introduction of IntersectionObserver API by Ruan Yifeng.
In normal development, we often need to know whether an element has entered a “viewport”, that is, whether it can be seen by the user.
The green square in the image above is constantly scrolling, and its visibility is indicated at the top.
The traditional implementation method is to call the getBoundingClientRect() method of the target element (green block) after listening to the Scroll event to get its coordinates corresponding to the upper left corner of the viewport, and then judge whether it is in the viewport. The disadvantage of this method is that it is easy to cause performance problems due to the intensive occurrence of Scroll events and large amount of computation.
There is a new IntersectionObserver API that can automatically “observe” whether elements are visible, and Chrome 51+ already supports it. Because the nature of visible is that a target element intersects with the viewport, the API is called a intersecting viewer.
use
It’s also very simple to use.
var io = new IntersectionObserver(callback, option);
Copy the code
In the above code, IntersectionObserver is the constructor provided natively by the browser and takes two parameters: callback is the callback function when visibility changes and option is the configuration object (this parameter is optional).
The return value of the constructor is an observer instance. The observe method of the instance specifies which DOM node to observe.
// Start observing
io.observe(document.getElementById('container'));
// Stop observing
io.unobserve(element);
// Close the viewer
io.disconnect();
Copy the code
In the code above, the argument to Observe is a DOM node object.
Call this method multiple times if you want to observe multiple nodes.
io.observe(elementA);
io.observe(elementB);
Copy the code
Code implementation
After reviewing the relevant API, let’s implement lazy image loading based on IntersectionObserver:
const imgs = document.querySelectorAll('img') // Get all the target elements to be observed
var options = {}
function lazyLoad(target) {
const observer = new IntersectionObserver((entries, observer) = > {
entries.forEach(entrie= > {
if (entrie.isIntersecting) {
const img = entrie.target;
const src = img.getAttribute('data-src');
img.setAttribute('src', src)
observer.unobserve(img); // Stop listening for images that have started loading
}
})
}, options);
observer.observe(target)
}
imgs.forEach(lazyLoad)
Copy the code
img.loading=lazy
The last one is much simpler, which is Chrome’s native LazyLoad property. Let’s take a look at its support across the major browsers:
In fact, the level of support is not particularly good, we your application for browser compatibility requirements are relatively high, suggest or wait and see a wave ~
It’s also pretty simple to use, as the title suggests:
<img src="example.jpg" loading="lazy" alt="zhangxinxu" width="250" height="150">
Copy the code
For more information about native lazy loading= “lazy”, please refer to Zhang Xinxu’s browser IMG images native lazy loading= “lazy” practice guide.