Use the Intersection Observer API to detect whether an element appears in the visual window

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

Local path

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
}