Intersection Observer API for Element Visibility, Intersection Detection, and Improved Performance
Today, on most web pages, even the optimized images can load up with significant delay. This increases the waiting time of visitors on your page before they can access its content.
Users would get impatient and navigate elsewhere unless you develop a solution to load images with super speed.
The conventional position calculation mechanisms on a dynamic website rely on explicit DOM state queries that cause expensive and frequent style recalculation and layout changes.
Consider a web page using infinite scrolling, and a vendor-provided library for advertisements management placed periodically throughout the page. This might have animated graphics at places and may use a custom library that draws notification boxes. Each of these has its intersection detection routines, all running on the main thread.
The significant performance overhead this causes through continuous polling leads to image loading, page loading, and scrolling delay.
Lazy loading allows for asynchronously loading images on websites, especially the image-heavy ones. These websites may load only a limited number of images. While scrolling down the page, placeholder images quickly fill up with real images for preview.
However, many sites abuse scroll handlers, perform multiple readback invocations and resort to expensive plugin-based solutions to compute “true” element visibility.
The Intersection Observer API addresses the above issues by asynchronously querying an element’s position respective to other elements and delivering this information at a slight delay, without perfect compositing-result data.
It asynchronously observes the intersection changes of a target element with an ancestor element or with a top-level document’s viewport. This helps lazy load an image gallery with the markup for each image looking like this:
Here, a data-src attribute contains the image path and not the src attribute. This delays the image from loading right away.
Why the Intersection Observer API?
Implementing intersection detection involved event handlers and calling methods for loops like Element.getBoundingClientRect() to build up the element-wise requisite information.
The main thread is used to run all this code, causing performance issues.
The IO API eliminates the need for time-consuming DOM and style queries, continuous polling, and custom plugins usage.
Developers have been talking about the Intersection Observer API with the changing browser support requirements.
This API makes it easy to watch and register callbacks that trigger the visibility of elements on a page when required. Also, intersection information is useful in many scenarios like:
- Lazy loading of images.
- Infinite list or page scrolls
- Prefetching of screen links as they appear.
- To track and detect if an ad was viewed or not.
- To determine an article’s readership and its extent
- To run expensive animations and renderings only when they’re visible on the screen.
Conventional methods like Scroll Event Listeners detect an element’s visibility or the relative visibility of two relative elements, but they make the process unreliable at times.
The browser and the sites the user is accessing also tend to become sluggish because of the CPU time and resources taken to complete the task.
Intersection Observer is less resource-intensive and easier to implement than checking if an element is about to enter the viewport and listen for scroll events.
It is very powerful where intersectionObserver does most of the complex calculations internally.
Being multi-browser compatible, the IO API supports native lazy loading, which gives the option to delay iframes and off-screen images until users scroll to their location on the page.
Scroll listener vs. Intersection Observer
The scroll listener could be implemented based on two factors, offset caching and throttling.
Offset caching saves the relative sections’ offset distance on a page, which changes based on the viewport width.
Throttling is done at a regular interval (for example, 300 ms) on the listener’s callback.
While scrolling continuously from the top to the very bottom of a web page, the following is bound to happen with the Scroll Listener.
- The computational stress becomes higher, which increases the load on the main thread.
- The Frames per second on the page could get lower and less steady, impacting the user’s experience on a web page.
- The callback time going beyond one Event loop could take longer, and this could become more frequent.
The total time spent performing scripting would be close to 28% for a Scroll Listener implementation and close to 23% when the Intersection Observer is implemented.
The code registers a callback function with the Intersection Observer API that executes whenever the monitored element enters or exits the root element (or the viewport).
This way, sites no longer need to rely on the main thread to watch for element intersection when scrolling or image/content loading happens. The main thread gets more free time for user input response, even on extremely slow machines.
How using the Intersection Observer reduced High CPU Utilization for Infinite Scrolling
Recently at the Pepperfry development center, our Listing page had slowed down immensely because of infinite scroll, lazy loading of images, a sluggish image load plugin, and high CPU utilization.
We discovered that the use of Scroll Event Listeners affected the performance of the site. Implementing the Intersection Observer API solved these issues at once.
Problem Statement
While using Firefox, the CPU utilization kept increasing for scrolling events on the Studio page, which resulted in constant system hangs.
This happened because the code was written using the Scroll Event Listener for detecting intersections.
The Solution
We replaced the Scroll Event Listener in the code with the Intersection Observer API, and it worked like a charm.
It reduced the high CPU utilization by at least 20%. We decided to replace the Scroll Event Listener with IO for tasks like lazy loading, infinite scroll, and making elements sticky after a certain point.
Here’s a comparison of the code we developed for the sticky button functionality using the scroll event listener and its replacement using IO.
Scroll Event Listener — The Sticky functionality runs every time the user scrolls.
The Intersection Observer API helps asynchronously observe changes in a target element’s intersection with an ancestor element or with a top-level document’s viewport.
The Intersection Observer API uses thresholds rather than reporting every possible change in how much a target element is visible. These thresholds can be set by providing one or more numeric values representing percentages of the target element that are visible. Then, the API only reports changes to visibility which cross these thresholds.
The following two lines are core to an Intersection Observer code.
The key steps in using the Intersection Observer include:
1. First, an Observer is created with some options.
2. Then, the Observer is triggered to start observing a target.
3. When the desired intersection happens, the callback function is called.
As a developer, a firm grasping on the following is essential for IO implementation.
Image source: medium.com
- root is the ancestor element used for observing an intersection; for example, the outer rectangle or the rectangle.
- target is the element being observed.
- threshold gives you the limit until which the overlap can occur.
- rootMargin is the margin of the root applied before calculating the extent of the intersection.
Making Intersection Observer more Efficient with the Intersection Change ‘callback’
When the value of a target element visible within the root element crosses one of the visibility thresholds, the IntersectionObserver
object's callback is executed. The callback receives an array of all of IntersectionObserverEntry
objects as input. One object is assigned for each threshold crossed, and a reference is set to the IntersectionObserver
object itself.
The thresholds list is an IntersectionObserverEntry
with each entry describing how much of a given element is intersecting with the root element, whether or not the element is intersecting, and the direction of the transition's occurrence.
The code first checks if the transition is a positive one, then it determines whether intersectionRatio
is above the set threshold, in which case it increments the counter.
Conclusion
There may be times when a Scroll Listener might be the best option due to external limitations.
But for dynamic tasks like changing the hash, making something stick after a certain point, and altering some styles based on scrolling, the Intersection Observer API is more promising for optimal app performance.
Thanks to Suman, Nilesh and our technical writer Sowmya Narayanan.
At Pepperfry, we always endeavor to bring programming innovation to the E-commerce space.
If you have any great ideas to share about the Interface Observer API, connect with us @Pepperfry today. We’d love to hear from you and enrich our digital discovery path.
Happy Programming to you!