@thednp/position-observer
v1.1.2
Published
🏯 PositionObserver is a JavaScript tool that provides a way to asynchronously observe changes in the position of a target element within its viewport.
Maintainers
Readme
PositionObserver
The PositionObserver is a lightweight utility that replaces traditional resize and scroll event listeners. Built on the IntersectionObserver API, it provides a way to asynchronously observe changes in the position of a target element with an ancestor element or with a top-level document's viewport.
Installation
npm i @thednp/position-observerbun add @thednp/position-observerpnpm add @thednp/position-observerdeno add npm:@thednp/position-observer@latestUsage
import PositionObserver from '@thednp/position-observer';
// Find a target element
const myTarget = document.getElementById('myElement');
// Define a callback
const callback = (entries: IntersectionObserverEntry[], currentObserver: PositionObserver) => {
// Access the observer inside your callback
// console.log(currentObserver);
entries.forEach((entry) => {
if (entry.isIntersecting/* and your own conditions apply */) {
// Handle position changes
console.log(entry.boundingClientRect);
}
})
};
// Set options
const options = {
root: document.getElementById('myModal'), // Defaults to document.documentElement
rootMargin: '0px', // Margin around the root, this applies to IntersectionObserver only
threshold: 0, // Trigger when any part of the target is visible, this applies to IntersectionObserver only
};
// Create the observer
const observer = new PositionObserver(callback, options);
// Start observing
observer.observe(myTarget);
// Example callback entries
[{
target: <div#myElement>,
boundingClientRect: DOMRectReadOnly,
intersectionRatio: number,
isIntersecting: boolean,
// ... other IntersectionObserverEntry properties
}]
// Get an entry
observer.getEntry(myTarget);
// Stop observing a target
observer.unobserve(myTarget);
// Resume observing
observer.observe(myTarget);
// Stop all observation
observer.disconnect();Instance Options
| Option | Type | Description |
|--------| -----|-------------|
| root | Element | undefined | The element used as the viewport for checking target visibility. Defaults to document.documentElement.|
| rootMargin | string | undefined | Margin around the root of the IntersectionObserver. Uses same format as CSS margins (e.g., "10px 20px"). |
| threshold | number | number[] | undefined | Percentage of the target's visibility required to trigger the IntersectionObserver callback. |
root
The PositionObserver instance.root identifies the Element whose bounds are treated as the bounding box of the viewport for the element which is the observer's target. Since we're observing for its width and height changes, this root can only be an instance of Element, so Document cannot be the root of your PositionObserver instance.
The IntersectionObserver instance.root is always the default, which is Document. The two observers really care for different things: one cares about intersection the other cares about position, which is why the two observers cannot use the same root.
When observing targets from a scrollable parent element, that parent must be set as root. The same applies to embeddings and IFrames. See the ScrollSpy example for implementation details.
The two initialization options specifically for the IntersectionObserver are rootMargin and threshold; they control the behavior of PositionObserver in the sense that when conditions are met for intersection, the PositionObserver entries are updated or skipped.
How it Works
- Initialization: Requires a valid callback function, or it throws an Error.
- Target Validation: The
observe()method requires a validElement, or it throws an Error. Targets not attached to the DOM are ignored. - Observation: Tracks changes in the target's top or left position relative to the root, as well as the root's
clientWidthandclientHeight. - Intersection Checks: Uses
IntersectionObserverwith thedocumentas the root to determineisIntersecting. TherootMarginandthresholdoptions apply to these checks.
Notes
- Performance: Use
entry.boundingClientRectfromobserver.getEntry(target)to avoid redundantgetBoundingClientRect()calls. - Async Design: Leverages
requestAnimationFrameandIntersectionObserverfor efficient, asynchronous operation. Consider wrapping callbacks inrequestAnimationFramefor synchronization and to eliminate any potential observation errors. - Visibility: Targets must be visible (no
display: noneorvisibility: hidden) for actual accurate bounding box measurements. - Cleanup: Call unobserve() or disconnect() when observation is no longer needed to free resources.
- ResizeObserver Alternative: Filter callbacks on
entry.boundingClientRect.widthor height changes to mimicResizeObserver. - Scroll Optimization: For scroll-specific changes, filter callbacks on
entry.boundingClientRect.toporleft. - IntersectionObserver Root: The underlying
IntersectionObserveruses thedocumentas its root, whilethe PositionObserver's root option defines the referenceElementfor position tracking. - IntersectionObserverEntry Spread: This is an interface instance and cannot be spread.
- RootMargin and Threshold: These options have no impact in
allmode, as non-intersecting targets are still processed. They are however relevant inintersectingorupdatemodes for defining visibility conditions.
Special Thanks
- Bart Spaans for his awesome contributions.
License
The PositionObserver is released under the MIT license.
