IntersectionObserver
Obserwuje, czy element jest widoczny w oknie albo w przewijanym kontenerze, i wywołuje funkcję, gdy to się zmienia.
- Zwraca
- Nowy obiekt
IntersectionObserver.
Przykład
#const feed = document.querySelector('#feed')
const lessons = feed.querySelectorAll('p')
const observer = new IntersectionObserver((entries) => {
for (const entry of entries) {
const state = entry.isIntersecting ? 'widoczna' : 'ukryta'
console.log(`${entry.target.textContent}: ${state}`)
}
}, { root: feed })
lessons.forEach((lesson) => observer.observe(lesson))
console.log(`Obserwuję ${lessons.length} lekcji`)Pierwszy wpis pochodzi z kodu synchronicznego, a stan lekcji przychodzi później, w funkcji zwrotnej. „Pętle” widać tylko częściowo, ale przy domyślnym threshold: 0 wystarczy choćby jeden piksel. Przewiń listę w podglądzie, a w konsoli pojawią się kolejne zmiany.
Definicja i zastosowanie
#IntersectionObserver informuje, kiedy element pojawia się w widocznym obszarze albo z niego znika. Zamiast sprawdzać położenie elementów przy każdym przewinięciu strony, tworzysz obserwatora z funkcją zwrotną, a przeglądarka sama wywołuje ją we właściwym momencie. To podstawa leniwego wczytywania, nieskończonych list, animacji uruchamianych przy przewijaniu i liczenia wyświetleń.
Funkcja zwrotna dostaje tablicę obiektów IntersectionObserverEntry. Najważniejsze pola to target (obserwowany element), isIntersecting (czy element jest widoczny) i intersectionRatio (jaka część elementu jest widoczna, od 0 do 1). Pierwsze wywołanie następuje zaraz po observe(), więc od razu poznajesz stan początkowy.
Opcje podajesz w drugim argumencie konstruktora. root to element, względem którego liczona jest widoczność (domyślnie okno przeglądarki). rootMargin rozszerza lub zwęża ten obszar, np. "200px" pozwala zacząć wczytywanie, zanim element pojawi się na ekranie. threshold określa, przy jakiej części widocznej powierzchni wywołać funkcję, np. 0.5 albo [0, 0.5, 1].
Metoda observe() dodaje element do obserwacji, unobserve() go usuwa, a disconnect() kończy całą obserwację. Funkcja zwrotna działa asynchronicznie, więc jej wpisy w konsoli pojawią się po kodzie synchronicznym.
Składnia
#new IntersectionObserver(callback)
new IntersectionObserver(callback, options)
observer.observe(element)
observer.unobserve(element)
observer.disconnect()Parametry
#callback
function (entries, observer)
Funkcja wywoływana przy zmianie widoczności. Dostaje tablicę wpisów i samego obserwatora.options
opcjonalny obiekt
root(element albonulldla okna),rootMargin(margines zapisany jak w CSS, np."0px 0px 200px 0px") ithreshold(liczba albo tablica liczb od 0 do 1).
Więcej przykładów
#const viewport = document.querySelector('#viewport')
const observer = new IntersectionObserver((entries) => {
for (const entry of entries) {
const percent = Math.round(entry.intersectionRatio * 100)
console.log(`${entry.target.textContent}: widać ${percent}%`)
}
}, { root: viewport, threshold: [0, 0.25, 0.5, 0.75, 1] })
viewport.querySelectorAll('.card').forEach((card) => observer.observe(card))Druga karta wystaje poza kontener, więc widać tylko jej ćwierć. Przewiń kontener w podglądzie, a obserwator odezwie się za każdym razem, gdy widoczna część przekroczy jeden z progów z threshold.
const list = document.querySelector('#list')
const sentinel = document.querySelector('#sentinel')
let page = 0
const observer = new IntersectionObserver(([entry]) => {
if (!entry.isIntersecting) return
page += 1
for (let i = 1; i <= 5; i++) {
const item = document.createElement('li')
item.textContent = `Zadanie ${(page - 1) * 5 + i}`
sentinel.before(item)
}
console.log(`Wczytano stronę ${page}`)
if (page === 3) {
observer.disconnect()
sentinel.textContent = 'To już wszystkie zadania'
}
}, { root: list })
observer.observe(sentinel)Na końcu listy stoi element-strażnik. Gdy staje się widoczny, dokładamy kolejne zadania przed nim. Przewiń listę do końca w podglądzie, żeby wczytać następne strony. Po trzeciej obserwator się rozłącza.
const observer = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue
const img = entry.target
img.src = img.dataset.src
observer.unobserve(img)
}
}, { rootMargin: '200px' })
document.querySelectorAll('img[data-src]').forEach((img) => observer.observe(img))Bez opcji root widoczność jest liczona względem okna przeglądarki. Obrazek dostaje prawdziwy adres dopiero wtedy, gdy znajdzie się 200 px od ekranu, a potem przestaje być obserwowany. W prostych przypadkach to samo daje atrybut HTML loading="lazy".
Obsługa przeglądarek
#Szeroko dostępne · od 2019 roku
Działa we wszystkich nowoczesnych przeglądarkach, także na telefonach. Możesz używać bez obaw.
- Chrome
- Edge
- Firefox
- Safari
Dobre praktyki
#- Po jednorazowej akcji, np. wczytaniu obrazka, wywołaj
observer.unobserve(element). Obserwator nie będzie bez potrzeby śledził elementu dalej. - Jeden obserwator może śledzić wiele elementów. Nie twórz osobnego obserwatora dla każdej pozycji listy.
- Do samego leniwego wczytywania obrazków często wystarczy atrybut
loading="lazy"w HTML. Obserwatora użyj, gdy potrzebujesz własnej logiki.
Powiązane hasła
#- ResizeObserverWywołuje funkcję za każdym razem, gdy zmieni się rozmiar obserwowanego elementu, bez względu na przyczynę zmiany.
- MutationObserverObserwuje zmiany w drzewie DOM: dodane i usunięte elementy, zmiany atrybutów i tekstu, i zgłasza je w jednej funkcji.
- Element.getBoundingClientRect()Zwraca rozmiar elementu i jego położenie względem widocznego obszaru okna w postaci obiektu DOMRect.
- Element.scrollIntoView()Przewija stronę i przewijane kontenery tak, żeby element pojawił się na ekranie, skokiem albo płynnie.
- HTMLElement.datasetObiekt z wartościami atrybutów data-* elementu, do odczytu i zapisu własnych danych.
Widzisz błąd albo brakuje przykładu? Napisz do nas.