When developing a web page, it is often necessary to know whether an element has entered a “viewport,” or whether the user can see it.

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

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.

// observe IO. Observe (document.getelementbyid ('example')); // Stop observing IO. Serve (element); // Close the observer 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

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.

Third, IntersectionObserverEntry object

IntersectionObserverEntry object provides a target element of information, a total of six attributes.

{time: 3893.92, rootBounds: ClientRect {bottom: 920, height: 1024, left: 0, right: 1024, top: 0, width: 920 }, boundingClientRect: ClientRect { // ... }, intersectionRect: ClientRect { // ... }, intersectionRatio: 0.54, target: element}Copy the code

The meaning of each attribute is as follows.

  • time: The time at which visibility changes, a high-precision timestamp in milliseconds
  • target: The object element being observed is a DOM node object
  • rootBounds: information about the rectangular region of the root element,getBoundingClientRect()Method, if there is no root element (that is, scrolling directly with respect to the viewport)null
  • boundingClientRect: Information about the rectangular region of the target element
  • intersectionRect: Information about the area where the target element intersects with the viewport (or root element)
  • intersectionRatio: Visibility ratio of the target element, i.eintersectionRectAccount forboundingClientRectIs fully visible1Is less than or equal to completely invisible0

In the image above, gray horizontal boxes represent viewports and dark red areas represent the four target elements being observed. All intersectionRatio diagrams of them have been indicated.

I wrote a Demo to show IntersectionObserverEntry object. Note that this Demo will only run on Chrome 51+.

Example: Lazy load

Sometimes we want static resources (such as images) to be loaded only when the user scrolls down into the viewport to save bandwidth and improve web page performance. This is called “lazy loading”.

With the IntersectionObserver API, this is easy to implement.

function query(selector) { return Array.from(document.querySelectorAll(selector)); } var observer = new IntersectionObserver( function(changes) { changes.forEach(function(change) { var container = change.target; var content = container.querySelector('template').content; container.appendChild(content); observer.unobserve(container); }); }); query('.lazy-loaded').forEach(function (item) { observer.observe(item); });Copy the code

In the code above, the template content is inserted into the real DOM only if the target area is visible, causing the loading of static resources.

Five, example: infinite scrolling

The implementation of infinite Scroll is also very simple.

Var intersectionObserver = new intersectionObserver (function (entries) {if (entries[0]. IntersectionRatio is returnedCopy the code

For infinite scrolling, it’s best to have a page end column (aka sentinels) at the bottom of the page. Once the footer is visible, the user reaches the bottom of the page and loads a new entry in front of the footer. The advantage of this is that the existing IntersectionObserver can be kept in use without having to call the observe() method again.

Option object

The second argument to the IntersectionObserver constructor is a configuration object. It can set the following properties.

6.1 threshold attribute

The threshold attribute determines when the callback function is triggered. It is an array and each member is a threshold value, which defaults to [0], i.e., intersectionRatio triggers the callback function when it reaches 0.

new IntersectionObserver( entries => {/* ... */}, {threshold: [0, 0.25, 0.5, 0.75, 1]});Copy the code

The user can customize the array. For example, [0, 0.25, 0.5, 0.75, 1] means that the callback is triggered when the target element is 0%, 25%, 50%, 75%, 100% visible.

6.2 Root attribute: rootMargin attribute

Many times, the target element will scroll not only with the window, but also inside the container (such as in an iframe window). Scrolling within the container also affects the visibility of the target element, as shown in the diagram at the beginning of this article.

The IntersectionObserver API supports intra-container rolling. The root attribute specifies the container node on which the target element is located (the root element). Note that the container element must be the ancestor node of the target element.


var opts = { 
  root: document.querySelector('.container'),
  rootMargin: "500px 0px" 
};

var observer = new IntersectionObserver(
  callback,
  opts
);
Copy the code

In addition to the root attribute, there is the rootMargin attribute in the above code. The latter defines the margin of the root element, which is used to expand or reduce the size of the rootBounds rectangle and thus affect the intersectionRect area. It uses CSS definitions such as 10px, 20px, 30px, 40px for top, right, bottom, and left.

This allows the viewer to be triggered whenever the visibility of the target element changes, whether it is window scrolling or container scrolling.

Seven, pay attention

The IntersectionObserver API is asynchronous and does not trigger with the rolling synchronization of the target element.

According to the specification, IntersectionObserver should be implemented using requestIdleCallback(), that is, only when the thread is idle will the observer be executed. This means that the observer has a very low priority and only executes when the browser is free after other tasks have been executed.

8. Reference links

  • IntersectionObserver’s Coming into View
  • Intersection Observers Explained

(after)