IntersectionObserver implements image lazy loading and infinite scrolling
background
The demand for lazy loading of images and scrolling data has always existed. The more common method is to monitor the scrolling event of the page or container, and calculate the relationship between the node and the container boundary in real time to realize different loading logic. Usually, EventListener
is used to bind listening events, and Element.getBoundingClientRect()
is used to obtain the boundary information of related elements. Then both are running on the main thread, which will occupy a certain amount of resources and performance. Therefore, frequently triggering events and calling methods may cause performance problems. Problem, this detection method is extremely weird and inelegant. The Intersection Observer API provides a method for asynchronously detecting changes in the size of the intersection area between the target element and ancestor elements or viewports. It registers a callback function when the size of the intersection area of the two monitored elements changes (including the monitored element entering the or exit another element), the callback method will be triggered to execute. In this way, the main thread will not have to waste resources monitoring scrolling and real-time calculation, and the browser will optimize the element intersection management by itself.
Introduction to the Intersection Observer API
Create an instance of Intersection Observer and add a listener object:
const el = documen.querySelector('#target') // 被监听的元素
const observer = new IntersectionObserver(callback, options) // 创建实例
observer.observe(el) // 开始监听元素
Among them, callback
is a callback function that must be configured, options
is used to configure the listening environment of the observer instance, and observe()
is used to register the listener. MDN document
Intersection observer options
If options are not specified, the observer instance will use the current viewport as the root, no margin, and a threshold of 0, which means that even a pixel change will trigger the callback function. The configurable parameters (sectioned from MDN) are as follows:
root
Specifies the root ( root ) element for checking the visibility of the target. Must be the parent element of the target element. If not specified or null
, defaults to browser window
rootMargin
The margin of the root ( root ) element. Similar to the margin
property in CSS, such as " 10px 20px 30px 40px"
(top, right, bottom, left). If the root parameter is specified, the rootMargin can also use a percentage to take the value. This property value is used when the intersection of the root element and the target occurs. The area range for calculating the intersection, use this property to control the shrinkage or expansion of each side of the root element. The default value is 0
threshold
It can be a single number or an array of numbers. When the intersection of the target element and the root element reaches this value, the callback function registered by IntersectionObserver will be executed. If you just want to detect when the target element's visibility in the root element exceeds 50%, you can specify a value of 0.5 for this property. If you want the target element to execute a callback every 25% of the visibility of the root element, then you can specify an array [0, 0.25, 0.5, 0.75, 1]
, the default value is 0. A value of 1.0 means that the callback will only be executed when the target is completely inside the root element.
Intersection observer callback
The callback function will be called when the following conditions occur:
- When Observer listens to the target element for the first time ( observe() method is executed)
- Whenever the target ( target ) element intersects with the device window or other specified element ( root ) (after the element's visible scale exceeds the specified threshold)
When the callback function is called, it will receive two parameters:
entries
: A array of IntersectionObserverEntry
objects
observer
: IntersectionObserver
instance called
IntersectionObserverEntry
IntersectionObserverEntry
describes the intersection state of the target element and its root element container at a specific transition moment. An instance of IntersectionObserverEntry
is passed as the entries
parameter to a callback function of a IntersectionObserver
instance, which has the following read-only properties:
boundingClientRect
: Returns DOMRectReadOnly
containing the bounds information of the target element. The bounds are calculated in the same way as Element.getBoundingClientRect()
intersectionRatio
: returns the ratio of intersectionRect
and boundingClientRect
, ranging from 0 to 1
intersectionRect
: returns a DOMRectReadOnly
used to describe the intersection area of the root and target elements
isIntersecting
: Returns a boolean value describing whether the target element has an intersection with the root element
rootBounds
: returns a DOMRectReadOnly
used to describe the root element in the intersection observer
target
: Elements whose intersecting area changes with the root ( Element
)
time
: Returns a record from the time origin of IntersectionObserver
origin ) to the timestamp of the time the intersection was triggered ( DOMHighResTimeStamp
)
IntersectionObserver
IntersectionObserver has the following methods:
disconnect()
: stop all listeners of the instance
observe()
: start listening
takeRecords()
: Returns an array of IntersectionObserverEntry
objects of all observation targets
unobserve()
: Stop listening for a specific element
Intersection Observer API in action
Image lazy loading
When we visit a web page with a lot of pictures, the loading speed is often slow because of the many pictures, and a large number of img pictures cause the page rendering to be blocked. By the time it took a lot of effort to load all the images and pages, the user was long gone. On the other hand, if the user only views the front part of the web page and then leaves, many images that have been loaded but are not displayed in the viewport area because they are at the bottom of the web page will greatly increase the pressure on the server, but the user does not look at it, it is wasted performance. In order to solve the above problems, it is necessary to introduce lazy loading of images. Lazy loading is actually very easy to understand. The key point is the word 'lazy'. When the user scrolls the corresponding visible area, if there are pictures in the visible area, they will be loaded, and the pictures that have not been loaded outside the visible area will not be loaded first. If the user scrolls the visible area to them, they will be loaded, otherwise they will not be loaded at all. . This greatly improves the performance of web page rendering and reduces unnecessary waste.
With the Intersection Observer API, we can easily implement lazy loading of images. This example is implemented using native HTML+JavaScript. The general idea is as follows:
Image placeholder: Here we use the HTML5 dataset attribute to assign the real image address to the
data-src
attribute<img data-src="../static/imgs/img-1.png" src="../static/imgs/loading.png">
Create an instance of Intersection Observer and configure the callback function
const observer = new IntersectionObserver(entries => { for (const i of entries) { if (i.isIntersecting) { // 当目标元素出现在视图内 const img = i.target; const trueSrc = img.getAttribute("data-src"); setTimeout(() => { img.setAttribute("src", trueSrc); // 方便展示懒加载效果 }, 1500); observer.unobserve(img); // 停止监听此元素 } } });
When initializing the instance, you can use the arrow function to directly bind the callback function. In the callback function, after setting the real address of the picture, we should stop monitoring the picture to avoid unnecessary processing.
Listen to all img elements
const images = document.getElementsByTagName("img"); for (const i of images) { observer.observe(i); }
It should be noted that the setting monitoring should be executed after the page structure rendering is completed, that is, the script should be executed when window.onload
, because when observer.observe()
is called, the callback function will be executed once. At this time, the element is not initialized, and the height is 0 if
The script will execute. If you cannot listen to the window.onload
event, you can set the basic width and height of the element to ensure that only some elements are in the view when the script is initialized. The actual effect is as follows:
It can be seen that after refreshing the page, only three to four images appearing in the view are loaded for the first time. As the page scrolls, the src
attribute of the img
tag that appears is changed to the real image address.
infinite scroll
Infinite scrolling of content is actually to actively load new data when the user scrolls to the bottom of the content, without the need for the user to operate the page, giving the user an illusion that the web page can scroll infinitely. Two implementation ideas are provided here, both of which are related judgments and operations in the callback function of Intersection Observer, which can be implemented by yourself:
- Store the list of image information, and judge in the callback function that if the current element is the last item in the list, trigger the loading of the data on the next page, and the newly loaded data can be merged with the original list. After loading the data, the newly added element must be monitored.
- The page is added to the footer bar (also known as sentinels). Once the footer bar is visible, it means that the user has reached the bottom of the page, thereby loading new data and placing it in front of the footer bar
The following picture is an example of infinite loading. Using Unsplash's API, several random pictures will be loaded each time:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。