Visibility Engine
createVisibilityEngine(pool?) — wraps a pooled IntersectionObserver with enter/leave edge detection and once support, without any framework-specific reactivity.
function createVisibilityEngine(pool?: ObserverPool): VisibilityEnginepool defaults to the shared observerPool singleton (see below) — pass your own new ObserverPool() to isolate observers from the rest of the app, which is mainly useful in tests.
Return value
observe(element, options, callback)
(element: Element, options: VisibilityObserveOptions, callback: (info: IntersectionInfo) => void) => () => void
Starts observing element. Returns an unsubscribe function.
destroy()
() => void
Runs every registered unsubscribe function and clears them.
VisibilityObserveOptions
| Field | Type | Default |
|---|---|---|
root | Element | Document | null | null |
rootMargin | string | '0px' |
threshold | number | number[] | 0 |
once | boolean | undefined — when true, observe() unobserves the element automatically after the first isIntersecting: true. |
onEnter | (info: IntersectionInfo) => void | — called when info.edge is 'enter-top' or 'enter-bottom'. |
onLeave | (info: IntersectionInfo) => void | — called when info.edge is 'leave-top' or 'leave-bottom'. |
IntersectionInfo
Passed to callback (and to onEnter/onLeave) on every change:
| Field | Type | Notes |
|---|---|---|
isIntersecting | boolean | |
intersectionRatio | number | |
boundingClientRect | DOMRectLike | |
edge | IntersectionEdge | undefined | 'enter-top' | 'enter-bottom' | 'leave-top' | 'leave-bottom' — only set when isIntersecting differs from the previous call. |
Example:
import { createVisibilityEngine } from '@macrulez/inview-core'
const visibility = createVisibilityEngine()
const unobserve = visibility.observe(
document.querySelector('#hero')!,
{ threshold: 0.5, once: true },
(info) => {
if (info.isIntersecting) console.log('half visible')
},
)Observer Pooling
ObserverPool / observerPool — pools native IntersectionObserver instances by (root, rootMargin, threshold), so many elements observed with the same options share one observer instead of each spawning its own. createVisibilityEngine() uses the shared observerPool singleton by default.
class ObserverPool {
observe(
element: Element,
options: ObserverPoolOptions,
callback: (entry: IntersectionObserverEntry) => void,
): () => void
}
const observerPool: ObserverPoolObserverPoolOptions is { root?, rootMargin?, threshold? } — the same three fields listed above, with the same defaults (null/'0px'/0), applied when a new native IntersectionObserver is actually constructed for a given key. The pool disconnects and drops a native observer once its last observed element unobserves. In an environment without IntersectionObserver at all, observe() returns a no-op unsubscribe rather than throwing.
Looking for the reactive version? See Element Visibility for Vue, or React Hooks for React — both wrap this same engine and share the same pool.