Encapsulating DOM Exposure: From Scroll Listener to IntersectionObserver
This article describes how to abstract and improve DOM exposure tracking by first using a scroll listener with getBoundingClientRect and then refactoring to a more robust IntersectionObserver implementation, including code examples, configuration details, and compatibility considerations.
With the increasing demand for exposure tracking, the author extracted previously written exposure logic into a reusable module.
Initial Version
The logic relies on window.scroll events and getBoundingClientRect() to determine if an element is within the viewport.
Key code:
function buryExposure (el, fn) {
let elEnter = false; // dom是否进入可视区域
el.exposure = () => {
const { top } = el.getBoundingClientRect();
if (top > 0 && top < window.screen.availHeight) {
if (!elEnter) {
elEnter = true;
fn(el)
}
} else {
elEnter = false;
};
}
document.addEventListener('scroll', el.exposure);
}Two important points:
Checking the top margin against the viewport to decide exposure.
Using a flag ( elEnter) to avoid repeated callbacks while the element stays visible.
Rewrite with IntersectionObserver
When a requirement arose to track horizontal scrolling inside a container, the scroll‑based approach proved insufficient, and performance concerns about getBoundingClientRect were raised.
The author turned to window.IntersectionObserver, an asynchronous API that observes the intersection of a target element with an ancestor or the viewport.
Typical usage:
const io = new IntersectionObserver(callback, options)
io.observe(DOM)Configuration options:
const observerOptions = {
root: null, // viewport as root
rootMargin: '0px', // no margin
threshold: [...Array(100).keys()].map(x => x / 100) // trigger on every 0.01 change
}Callback example:
(entries) => {
entries.forEach(item => {
if (item.intersectionRatio > 0 && item.intersectionRatio <= 1) {
// partially visible → treat as exposure
} else if (item.intersectionRatio === 0) {
// not visible → reset
}
});
}The observer reports intersectionRatio values, where 1 means fully visible, values between 0 and 1 indicate partial visibility, and 0 means hidden. The isIntersecting property can also be used.
However, the author found that in some real‑world scenarios (e.g., a horizontally scrolling banner), the observer could report an element as intersecting even when the user cannot actually see it because a parent container clips it.
Final Version
To ensure broader compatibility, a polyfill is loaded when IntersectionObserver is unavailable:
// Use W3C polyfill
require('intersection-observer');The final encapsulated function:
/**
* DOM exposure
* @param {object} options 配置参数
* options.DOMs - array of DOM elements to observe
* options.callback - function to call on exposure
* options.parentDom - optional root element for observation
*/
export default function expose (options = {}) {
if (!options.DOMs || !options.callback) {
console.error('Error: 传入监听DOM或者回调函数');
return;
}
const observerOptions = {
root: null,
rootMargin: '0px',
threshold: [...Array(100).keys()].map(x => x / 100)
};
options.parentDom && (observerOptions.root = options.parentDom);
if (window.IntersectionObserver) {
let elEnter = false; // dom是否进入可视区域
const io = new IntersectionObserver((entries) => {
const fn = () => options.callback({ io });
entries.forEach(item => {
if (!elEnter && item.intersectionRatio > 0 && item.intersectionRatio <= 1) {
fn();
elEnter = true;
} else if (item.intersectionRatio === 0) {
elEnter = false;
}
});
}, observerOptions);
options.DOMs.forEach(DOM => io.observe(DOM));
}
}References
Further reading on getBoundingClientRect and IntersectionObserver can be found at the linked articles in the original post.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
政采云技术
ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
