IntersectionObserver báo bất đồng bộ khi một element đi vào (hoặc rời khỏi) viewport hay một container.
const io = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue
entry.target.src = entry.target.dataset.src // lazy load
io.unobserve(entry.target)
}
},
{
root: null, // null = viewport
rootMargin: '200px', // kích hoạt sớm 200px trước khi lọt vào màn hình
threshold: 0.5, // 50% element hiện ra mới tính là intersecting
},
)
document.querySelectorAll('img[data-src]').forEach((el) => io.observe(el))Vì sao hơn scroll listener: callback không chạy trên đường cuộn, browser tự gộp nhiều element, và không có getBoundingClientRect() mỗi khung hình gây layout thrashing.
Dùng cho: lazy load ảnh và iframe, cuộn vô hạn, kích hoạt animation khi cuộn tới, đo tỉ lệ hiển thị quảng cáo, đánh dấu mục đang đọc trong mục lục.
Lưu ý 1: callback luôn chạy một lần ngay sau observe() để báo trạng thái ban đầu — không kiểm tra isIntersecting sẽ tưởng element vừa cuộn tới.
Lưu ý 2: quên unobserve() sau khi xử lý xong khiến callback tiếp tục chạy mỗi lần element ra vào màn hình.
Ghi chú: với ảnh, <img loading="lazy"> đã đủ cho lazy load thông thường — chỉ cần IntersectionObserver khi muốn kiểm soát ngưỡng hoặc làm việc khác ngoài tải ảnh.