Hi there 🙌
what knowledge can we learn through this article?
- what the different between viewport and DOM Layout?
- width, innerWidth(window.innerWidth for viewport width), clientWidth, offsetWidth, scrollWidth?
- how to use getBoundingClientRect()
- how to use Intersection Observer API
- practical Application Scenarios
- Image/Component Lazy Loading
- Infinite Scrolling
- Element Exposure Statistics
use getBoundingClientRect()
getBoundingClientRect() is a native DOM method that returns a DOMRect object containing the element’s position relative to the top-left corner of the viewport (properties like top/bottom/left/right) and dimensions (width/height). It eliminates the need to manually accumulate offset values, simplifying calculations.
Judgment Conditions (Vertical Visibility Example)
- Partially visible: rect.top < window.innerHeight && rect.bottom > 0 (the element’s top is above the viewport’s bottom, and its bottom is below the viewport’s top)
- Fully visible: rect.top >= 0 && rect.bottom <= window.innerHeight
Code Example
function isInViewport(elem) {
const rect = elem.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// Check for partial visibility (vertical + horizontal)
return (
rect.top < viewportHeight &&
rect.bottom > 0 &&
rect.left < viewportWidth &&
rect.right > 0
);
}
// Optimization: Add throttling to the scroll event (avoid frequent calculations)
function throttle(fn, delay = 100) {
let lastTime = 0;
return () => {
const now = Date.now();
if (now - lastTime > delay) {
fn();
lastTime = now;
}
};
}
window.addEventListener('scroll', throttle(() => {
const target = document.getElementById('target');
if (isInViewport(target)) {
target.style.backgroundColor = 'red';
}
}));
use intersection observer API
The Intersection Observer is a browser-provided asynchronous observation API designed to detect the "intersection state" (whether they overlap, and the overlap ratio) between a "target element" and a "root element" (default: the viewport). It eliminates manual position calculations in scroll events and offers excellent performance (browser-internal optimizations avoid reflows/repaints), making it the preferred choice for modern front-end development.
Code Example(Basic Usage: Viewport Detection)
// 1. Create an Observer instance
const observer = new IntersectionObserver((entries) => {
// entries: Array of intersection details for observed elements (may include multiple elements)
entries.forEach(entry => {
// entry.isIntersecting: Whether the element intersects with the root (entered the viewport)
if (entry.isIntersecting) {
console.log('Element entered the viewport', entry.target);
// Optional: Stop observing (e.g., for lazy loading images, only need one detection)
observer.unobserve(entry.target);
} else {
console.log('Element left the viewport', entry.target);
}
});
}, {
// Configuration: Root = viewport, detect 100px early
rootMargin: '100px 0px',
// Trigger callback when overlap ratio reaches 10%
threshold: 0.1
});
// 2. Observe the target element
const target = document.getElementById('target');
observer.observe(target);
// 3. (Optional) Stop observing all elements (e.g., when a component is destroyed)
// observer.disconnect();
Advanced Usage: Observe a Custom Scroll Container
To detect if an element is within a custom scroll container (instead of the viewport), set root to the container element:
const scrollContainer = document.getElementById('scroll-container');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('Element entered the scroll container’s viewport');
}
});
}, {
root: scrollContainer, // Root = custom scroll container
rootMargin: '0px',
threshold: 1 // Trigger when fully visible
});
Pros & Cons
✅ Pros:
Asynchronous execution (does not block the main thread), ensuring excellent performance (no scroll event jank).
Supports early/late detection (rootMargin) and precise overlap ratio monitoring (threshold).
Concise code (no manual position calculations).
❌ Cons: Limited compatibility (supports IE11+, Chrome 51+, Firefox 55+); older browsers require a polyfill (e.g., the intersection-observer library via npm).
Top comments (0)