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 guysAPIIt 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.