Intersection Observer

교차 관찰자

It asynchronously reports how much of a target intersects the viewport.

···
html
<div class="v"><header>INTERSECTION <span id="ratio">0%</span></header><div class="stage viewport"><div class="sight">viewport<div class="target">observed card</div></div><div class="meter"><i id="fill"></i></div></div><div class="muted" id="notice">outside</div></div>
css
.v{width:min(94vw,760px);height:min(88vh,320px);padding:clamp(9px,2.4vmin,18px);border:1px solid var(--line);border-radius:12px;background:var(--surface);font:500 clamp(12px,2.4vmin,16px)/1.3 var(--font-sans,system-ui,sans-serif);display:flex;flex-direction:column;gap:clamp(5px,1.4vmin,10px);overflow:hidden}.v header{display:flex;align-items:center;justify-content:space-between;gap:8px;color:var(--accent);font-weight:700}.v header span{color:var(--muted);font-weight:500}.v .stage{flex:1;min-height:0;position:relative}.v .muted{color:var(--muted)}.v .active{background:color-mix(in srgb,var(--accent) 18%,var(--surface));border-color:var(--accent)!important;color:var(--fg)}.v button{font:inherit;color:var(--fg);background:var(--bg);border:1px solid var(--line);border-radius:7px;padding:5px 8px;cursor:pointer}.viewport{display:flex;align-items:center;justify-content:center;gap:12px}.sight{position:relative;width:63%;height:75%;border:2px dashed var(--accent);border-radius:8px;text-align:center;color:var(--muted);overflow:hidden}.target{position:absolute;left:25%;top:23%;width:55%;height:48%;border-radius:8px;background:var(--accent);color:var(--bg);display:grid;place-items:center;animation:cross 3s ease-in-out infinite alternate}@keyframes cross{from{transform:translateX(-150%)}to{transform:translateX(100%)}}.meter{width:12px;height:70%;border:1px solid var(--line);border-radius:10px;overflow:hidden;display:flex;align-items:end}.meter i{display:block;width:100%;height:0;background:var(--accent);transition:height .3s}
js
const target=document.querySelector(".target"),sight=document.querySelector(".sight");function measure(){const a=target.getBoundingClientRect(),b=sight.getBoundingClientRect();const width=Math.max(0,Math.min(a.right,b.right)-Math.max(a.left,b.left));const ratio=Math.round(width/a.width*100);document.getElementById("ratio").textContent=ratio+"%";document.getElementById("fill").style.height=ratio+"%";document.getElementById("notice").textContent=ratio?"intersecting":"outside";requestAnimationFrame(measure)}measure()

Intersection Observer reports when a target crosses intersection thresholds relative to a root. It avoids measuring element positions on every scroll event.

In the demo a card moves through an observation window, updating its ratio and state. Callbacks are asynchronous, so it is not for pixel-perfect scroll synchronization.

When to use

Use it for lazy images or animations triggered when content enters view.

Open as page ↗