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

A, API

It’s 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('example'));

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

Callback parameter

When the visibility of the target element changes, the observer’s callback function is called.

Callback typically fires twice. Once the target element has just entered the viewport (becoming visible), and once it has completely left the viewport (becoming invisible).


var io = new IntersectionObserver(
  entries= > {
    console.log(entries); });Copy the code

In the above code, the callback function is written as an arrow function. The parameters of the callback function (entries) is an array, each member is a IntersectionObserverEntry object. For example, if the visibility of two observed objects changes at the same time, the Entries array will have two members.

Iii. Case code

Without further ado, get right to the code


// lazy load listening events
const observer = new IntersectionObserver(
    function(entires) {
        entires.forEach(function(item) {
        if (item.isIntersecting){  // isIntersecting is a Boolean value that currently monitors whether the intersecting area of the element is within the specified threshold of the visible area
            item.target.src = item.target.getAttribute('data-src')
            // The resource is loaded and then stopped to observe
            observer.unobserve(item.target)
        }
        // Remember to close observer.disconnect() when uninstalling the component;}); });Copy the code

Since React operates on DOM elements and I prefer refs, we can store dom elements this way

    constrefs = []; Defines the combination of DOM elements to listen toconst img_contain = List.map((item,i) = > {
        let ref = "image_" + i;  // The ref name of the nodeRefs. Push (ref);return <img ref={ref} src="Default image" data-src={item.url} key={i} />
    }

Copy the code

How do we bind lazy loading listener events once we save dom elements?

// lazy load initialization
initLazyImg = (refs) = >{
    refs.forEach(el= >{
        observer.observe(this.refs[el]); })}render() {
    return <div>{/* lazy load initialization Settings listening */}<img style={{opacity:0}} src={refs.join(', ')}onError={()= >{this.initLazyImg(refs)}} />
    </div>
}

Copy the code

Last but not least, we need to uninstall this listening event when unregistering the component to prevent memory abuse

componentWillUnmount(){  // Triggered when components are uninstalled
    console.log("Turn off the viewer")
    observer.disconnect();
}

Copy the code

Case code Presentation

import React, { Component } from 'react';
// lazy load listening events
const observer = new IntersectionObserver(
    function(entires) {
        entires.forEach(function(item) {
        if (item.isIntersecting){  // isIntersecting is a Boolean value that currently monitors whether the intersecting area of the element is within the specified threshold of the visible area
            item.target.src = item.target.getAttribute('data-src')
            // The resource is loaded and then stopped to observe
            observer.unobserve(item.target)
        }
        // Remember to close observer.disconnect() when uninstalling the component;}); });export class GatherGallery extends Component {
    componentWillUnmount(){  // Triggered when components are uninstalled
            console.log("Turn off the viewer")
            observer.disconnect();
    }

    // lazy load initialization
    initLazyImg = (refs) = >{
        refs.forEach(el= >{
            observer.observe(this.refs[el]); })}render() {
        constrefs = []; Defines the combination of DOM elements to listen toconst img_contain = [{url:"page1.png"}].map((item,i) = > {
            let ref = "image_" + i;  // The ref name of the nodeRefs. Push (ref);return <img ref={ref} src="Default image" data-src={item.url} key={i} />
        }

        return <div>{img_contain} {/* Lazy load initialization Settings listener */}<img style={{opacity:0}} src={refs.join(', ')}onError={()= >{this.initLazyImg(refs)}} />
        </div>}}Copy the code

If there is a better way to deal with the document or what deficiencies, trouble big guy comments on ha, thank you for reading