Use the Intersection Observer API to detect whether an element appears in the visual window
API interpretation:
The Intersection Observer API provides a method to asynchronously detect changes in the intersection of a target element and its ancestor element or viewport (collectively referred to as the root element).
Note: Because this API is asynchronous, it will not be triggered synchronously with the scrolling of the target element. The IntersectionObserver API is implemented through requestIdleCallback(), that is, the observation will only be performed when the browser is idle. device.
Important concepts of Intersection observer
The Intersection observer API has the following five important concepts:
- target element – the element we want to listen to
- Root element – an element that helps us determine whether the target element meets the conditions
- In the following two cases, the root element will default to the viewport of the top-level document (usually html).
-
- When the target element is not a descendant of the scrollable element and no value is passed
-
- Specifies that the root element is null
- Intersection ratio-an expression of the intersection of the target element and the root relative to the target element as a percentage (value range 0.0-1.0).
- Threshold – The condition under which the callback function triggers.
- Callback function (callback) – A function configured for this API that will be triggered under set conditions.
Usage
It declares an object in the form of new
and receives two parameters callback
and options
const io = new IntersectionObserver(callback [,options]) io.observe(DOM)
callback
callback is a callback function that is triggered when the listening target scrolls changes after adding a listener. Receives one parameter entries, which is an IntersectionObserverEntry instance. Describes the intersection state of the target element and root. The specific parameters are as follows:
Attributes | Description |
---|---|
boundingClientRect | Returns the boundary information containing the target element. The return result is the same as element.getBoundingClientRect() |
intersectionRatio | Returns the occurrence of the target element The proportion of the visual area |
intersectionRect | Used to describe the intersection area between the root and the target element |
isIntersecting | Returns a Boolean value. The following two operations will trigger the callback: 1. If the target element appears in the root visual area, return true. 2. If it disappears from the root visual area, return false |
rootBounds | Used to describe the root in the intersection observer (intersection observer). |
target | Target element: the element whose intersection area changes with the root (Element) |
time | Returns a timestamp that records the time from the IntersectionObserver’s time origin to the time when the intersection is triggered |
The two attributes in bold in the table are commonly used judgment conditions: isIntersecting (whether it appears in the visible area) and intersectionRatio (the ratio that appears in the visual area)
options
Options is an object used to configure parameters, which can also be left blank. There are three attributes, as follows:
Attributes | Description |
---|---|
root | The specific ancestor element of the object being listened to. If no value is passed in or the value is null, the viewport of the top-level document (usually html) is used by default. |
rootMargin | The rectangular offset added to the root bounding box when calculating intersection, which can effectively reduce or expand the determination of the root. range to meet calculation needs. All offsets can be expressed in pixels (px) or percentages (%), and the default value is “0px 0px 0px 0px”. |
threshold | A list containing thresholds, arranged in ascending order. Each threshold in the list is the ratio of the intersection area to the boundary area of the monitoring object. . When any threshold of the listening object is crossed, the callback will be triggered. The default value is 0. |
Method
Having introduced so many configuration items and parameters, I almost forgot the most important one, what are the methods of IntersectionObserver? If you want to monitor certain elements, you must perform observe on the element.
Method | Description |
---|---|
observe() | Start monitoring a target element |
unobserve() | Stop monitoring a specific target element |
takeRecords() | Returns an array of IntersectionObserverEntry objects of all observation targets |
disconnect() | Makes the IntersectionObserver object stop all monitoring work |
Practical application
- Lazy loading of images
const imgList = [...document.querySelectorAll('img')] var io = new IntersectionObserver((entries) =>{<!-- --> entries.forEach(item => {<!-- --> // isIntersecting is a Boolean value to determine whether the target element is currently visible. if (item.isIntersecting) {<!-- --> item.target.src = item.target.dataset.src // Stop monitoring the element after the image is loaded. io.unobserve(item.target) } }) }) // observe traverses and monitors all img nodes imgList.forEach(img => io.observe(img))
- Buried exposure
If there is a requirement, a specific element on a page can only be exposed when it is completely displayed in the visible area.
const boxList = [...document.querySelectorAll('li')] var io = new IntersectionObserver((entries) =>{<!-- --> entries.forEach(item => {<!-- --> // intersectionRatio === 1 indicates that the element is fully exposed and meets business requirements if (item.intersectionRatio === 1) {<!-- --> // . . . Buried exposure code // do something... io.unobserve(item.target) } }) }, {<!-- --> root: null, threshold: 1, // The threshold is set to 1, and the callback function is triggered only when the ratio reaches 1. }) // observe traverses and monitors all box nodes boxList.forEach(item => io.observe(item))
- demo:
You can run the following code on your computer to have a deeper understanding.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>IntersectionObserver</title> <style> li {<!-- --> width: 200px; height: 400px; border: 1px solid gray; } </style> </head> <body> <ul> <li>1-aaa</li> <li>2-bbb</li> <li>3i-ccc</li> <li>4i</li> <li>5i</li> <li>6i</li> <li>7i</li> <li>8i</li> <li>9i</li> <li>10i</li> <li>l1</li> <li>l2</li> <li>l3</li> <li>l4</li> <li>l5</li> <li>l6</li> <li>l7</li> <li>l8</li> <li>l9</li> <li>10</li> <li>twenty one</li> <li>twenty two</li> <li>twenty three</li> <li>twenty four</li> <li>25</li> <li>26</li> <li>27</li> <li>28</li> <li>29</li> <li>30</li> </ul> <script> const imgList = [...document.querySelectorAll("li")]; const options = {<!-- --> root: null, rootMargin: "1px", thresholds: 1, }; //io is the IntersectionObserver object - created by the IntersectionObserver() constructor const io = new IntersectionObserver((entries) => {<!-- --> //entries is an array of IntersectionObserverEntry objects entries.forEach((item) => {<!-- --> //item is an IntersectionObserverEntry object // item.isIntersecting is a Boolean value to determine whether the target element is currently visible. if (item.isIntersecting) {<!-- --> console.log(item); // item.target.src = item.target.dataset.src; // Stop listening to the element after li is loaded io.unobserve(item.target); } // intersectionRatio === 1 indicates that the element is fully exposed // if (item.intersectionRatio === 1) {<!-- --> // Buried point exposure code // do something... // Stop listening to this element // io.unobserve(item.target); // } }); }, options); //No options parameter is passed, the default root element is the browser viewport // observe traverses and monitors all li nodes imgList.forEach((li) => io.observe(li)); </script> </body> </html>
Other methods on how to determine whether an element is within the visible area
The first method: offsetTop, scrollTop
Formula: el.offsetTop – document.documentElement.scrollTop <= viewPortHeight
function isInViewPortOfOne (el) {<!-- --> // viewPortHeight is compatible with all browser writing methods const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight const offsetTop = el.offsetTop const scrollTop = document.documentElement.scrollTop const top = offsetTop - scrollTop console.log('top', top) // There is + 100 here for early loading + 100 return top <= viewPortHeight + 100 }
Second method: getBoundingClientRect
- The return value is a DOMRect object with left, top, right, bottom, x, y, width, and height properties.
Formula: el.getBoundingClientReact().top <= viewPortHeight
In fact, el.offsetTop – document.documentElement.scrollTop = el.getBoundingClientRect().top, using this, we can use the following code instead of method 1
function isInViewPortOfTwo (el) {<!-- --> const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight const top = el.getBoundingClientRect() & amp; & el.getBoundingClientRect().top console.log('top', top) return top <= viewPortHeight + 100 }