A tiny TypeScript library to scroll an element into view unless it's already visible in the viewport.
npm install scroll-unless-visibleIntersectionObserver-first drop-in for scroll-into-view-if-needed. It scrolls only when an element isn’t sufficiently visible, with a geometry fallback for legacy environments.
scrollMode: "if-needed" | "always" to mirror the original APIboundary / root), margins, and thresholds``bash`
npm install scroll-unless-visibleor
bun add scroll-unless-visible
`ts
import { scrollIntoViewIfNeeded } from "scroll-unless-visible";
const card = document.querySelector(".card")!;
const result = await scrollIntoViewIfNeeded(card, {
scrollMode: "if-needed", // or "always"
behavior: "smooth",
block: "nearest",
inline: "nearest",
boundary: (el) => el.classList.contains("scroll-container"),
threshold: 1,
rootMargin: "8px",
});
// result => { visible: boolean; scrolled: boolean }
`
Other exports:
- scrollUnlessVisible(target, options?)isVisible(target, options?)
-
- scrollMode: "if-needed" (default) | "always"block
- , inline, behavior, scrollIntoViewOptions: forwarded to scrollIntoViewboundary
- : element, document, or predicate to choose the scroll container; defaults to viewportroot
- : explicit root for visibility (overrides boundary)threshold
- : required visible ratio (0–1), default 1rootMargin
- : margin around the root for visibility checks, default 0pxtimeoutMs
- : max wait for the first observer callback before falling back, default 150
- Prefers IntersectionObserver with the provided root; falls back to geometry when unavailable or timed out."nearest"
- Uses smooth scrolling and positioning by default to minimize layout jumps.
- Unit: npm run test:unit (Vitest, jsdom)npm run test:e2e
- E2E-style: (Vitest, jsdom)npm test`
- Full suite: