The IntersectionObserver
object, belonging to the Intersection Observer API
, provides an asynchronous way to observe the intersection status of a target element with its ancestor element or the top-level document viewport. The ancestor element and the viewport are referred to as the root. In other words, the IntersectionObserver API
can automatically observe whether an element is visible. Since the essence of visibility is the intersection between the target element and the viewport, this API is called an intersection observer. Compatibility: Check compatibility here.
IntersectionObserver
solves a long-standing problem on the web of observing whether elements are visible. The essence of visibility is the intersection between the target element and the viewport, which is why this API is called an intersection observer. Detecting whether an element is visible or whether two elements intersect is not easy. Many solutions are unreliable or have poor performance. There are now many requirements for intersection detection, such as lazy loading images, infinite scrolling content, detecting element exposure, playing animations within the visible area, etc. Intersection detection usually requires listening to the onscroll
event and may require frequent calls to Element.getBoundingClientRect()
to obtain the boundary information of relevant elements. Event listening and calling Element.getBoundingClientRect()
both run on the main thread, so frequent triggering and calling may cause performance issues. This detection method is extremely awkward and inelegant.
The Intersection Observer API
registers a callback function, which is triggered and executed whenever the monitored element enters or exits another element or the viewport, or when the size of the intersection area between two elements changes. This way, the website's main thread no longer needs to work hard to monitor element intersections, as the browser will optimize the intersection management. It's important to note that the Intersection Observer API
cannot provide the number of overlapping pixels or the specific pixels that overlap. Its more common usage is to trigger a callback when the intersecting ratio of two elements is around N%
to perform certain logic.
const io = new IntersectionObserver(callback, option);
// Start observing
io.observe(document.getElementById("example"));
// Stop observing
io.unobserve(element);
// Disconnect the observer
io.disconnect();
-
The parameter
callback
, when a newIntersectionObserver
object is created, will execute the specified callback function when it observes that the visible portion of the target element has crossed one or more thresholdsthresholds
. -
The parameter
option
, the second parameter of theIntersectionObserver
constructor is a configuration object, which can set the following properties:- The
threshold
property determines when to trigger the callback function. It is an array, each member is a threshold value, default as[0]
, that is, when the intersection ratiointersectionRatio
reaches0
, the callback function will be triggered. Users can customize this array, for example,[0, 0.25, 0.5, 0.75, 1]
indicating that the callback function will be triggered when the target element is0%
,25%
,50%
,75%
, and100%
visible. - The
root
property specifies the container node, the root element, where the target element is located. The target element will scroll not only with the window, but also within the container, for example, scrolling in aniframe
window, theroot
property needs to be set. Note that the container element must be an ancestor element of the target element. - The
rootMargin
property defines themargin
of the root element to expand or shrink the size of therootBounds
rectangle, thereby affecting the size of theintersectionRect
intersection area. It uses the CSS definition method, for example,10px 20px 30px 40px
, representing values in thetop
,right
,bottom
, andleft
directions.
- The
-
The property
IntersectionObserver.root
is read-only, representing the specific ancestor elementelement
of the observed object. If no value is passed or the value isnull
, the top-level document viewport is used by default. -
The property
IntersectionObserver.rootMargin
is read-only, representing the rectangle offset added to the rootroot
boundary box during intersection calculation, which can effectively shrink or expand the judging range of the root to meet calculation needs. The value returned by this property may differ from the value specified when calling the constructor, so it may be necessary to change the value to match internal requirements. All offset values can be expressed in pixelspx
or percentage (%) and the default is0px 0px 0px 0px
. -
The property
IntersectionObserver.thresholds
is read-only, representing a list of thresholds, sorted in ascending order. Each threshold in the list is the ratio of the intersecting area of the observed object to the boundary region. A notificationNotification
will be generated when any threshold of the observed object is crossed. If no value is passed into the constructor, the default value is0
. -
The method
IntersectionObserver.disconnect()
stops theIntersectionObserver
object from observing. -
The method
IntersectionObserver.observe()
starts observing a target element for theIntersectionObserver
. -
The method
IntersectionObserver.takeRecords()
returns an array ofIntersectionObserverEntry
objects for all observed targets. -
The method
IntersectionObserver.unobserve()
stops observing a specific target element for theIntersectionObserver
.
Additionally, when the callback
function is executed, it will pass an IntersectionObserverEntry
object parameter, which provides the following information:
-
time:
the time when the visibility changes, a high-precision timestamp in milliseconds. -
target:
the observed target element, aDOM
node object. -
rootBounds:
information about the rectangle area of the root element, the return value of thegetBoundingClientRect
method. If there is no root element, or directly scrolls relative to the viewport,null
is returned. -
boundingClientRect:
information about the rectangle area of the target element. -
intersectionRect:
information about the intersecting area of the target element with the viewport or root element. -
intersectionRatio:
the visible ratio of the target element, i.e. the ratio ofintersectionRect
toboundingClientRect
. It is1
when fully visible and less than or equal to0
when fully invisible.
Implement a simple example using IntersectionObserver
. Two squares can demonstrate whether square 1
is within the visible area of the screen and whether square 2
is within the relatively visible intersection area of square 1
. Additionally, IntersectionObserver
can be used to optimize the initial screen rendering, which can be referenced at https://github.com/WindrunnerMax/EveryDay/blob/master/Vue/Vue%E9%A6%96%E5%B1%8F%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E7%BB%84%E4%BB%B6.md
.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
margin: 0;
padding: 0;
height: 100vh;
width: 100vw;
overflow-x: hidden;
}
.flex{
display: flex;
}
.top-fixed{
top: 0;
position: fixed;
}
.placeholder1{
width: 100%;
}
#box1{
height: 200px;
overflow-y: auto;
border: 1px solid #aaa;
width: 60%;
}
.box1-placeholder{
height: 105vh;
}
#box2{
height: 100px;
background-color: blue;
margin-top: 300px;
width: 60%;
}
.box2-placeholder{
height: 205px;
}
</style>
</head>
<body>
<section class="flex top-fixed">
<div class="flex">BOX1:</div>
<div class="flex" id="box1-status">invisible</div>
<div class="flex"> BOX2:</div>
<div class="flex" id="box2-status">invisible</div>
</section>
<div class="box1-placeholder"></div>
<div id="box1">
<div class="box2-placeholder"></div>
<div id="box2"></div>
<div class="box2-placeholder"></div>
</div>
<div class="box1-placeholder"></div>
</body>
<script>
(function(){
const box1 = document.querySelector("#box1");
const box2 = document.querySelector("#box2");
const box1Status = document.querySelector("#box1-status");
const box2Status = document.querySelector("#box2-status");
const box1Observer = new IntersectionObserver(entries => {
entries.forEach(item => {
// `intersectionRatio` represents the visible percentage of the target element. If it's greater than `0`, it's visible
if (item.intersectionRatio > 0) {
box1Status.innerText = "visible";
}else{
box1Status.innerText = "invisible";
}
});
}, {root: document});
const box2Observer = new IntersectionObserver(entries => {
entries.forEach(item => {
// `intersectionRatio` represents the visible percentage of the target element. If it's greater than `0`, it's visible
if (item.intersectionRatio > 0) {
box2Status.innerText = "visible";
}else{
box2Status.innerText = "invisible";
}
});
}, {root: box1});
box1Observer.observe(box1);
box2Observer.observe(box2);
})();
</script>
</html>
https://github.com/WindrunnerMax/EveryDay
https://www.jianshu.com/p/eadd83d794c8
https://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html
https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver