IntersectionObserver: Sichtbarkeit von Elementen checken

IntersectionObservers

Mit IntersectionObservers ist es sehr einfach und vor allem ohne Jank möglich, herauszufinden, ob ein Element gerade im Viewport sichtbar wird bzw. ihn verlässt. Das Besondere an IntersectionObservers (IO) ist, dass man dafür weder getBoundingClientRect() abfragen, noch einen Scroll event handler anlegen muss.

IO sind gedacht und eignen sich für:

  • Abfragen der Position von below the fold Inhalten und lazy loaden dieser
  • Performante Infinite Scrolling Listen
  • Sichtbarkeit von Elementen abfragen. Besonders interessant für Werbebanner.

Sie eignen sich nicht für Szenarien bei denen pixelgenaue Informationen, bspw. bei Animation, gefragt sind.

IntersectionObserver erstellen

Man legt einen IntersectionObserver an und legt fest, welches Element observed werden soll.

const io = new IntersectionObserver(entries => {

    // Verfügbare Daten, wenn das Element den Viewport betritt oder verlässt
    console.log(entries);
});

// Element, das observed werden soll
const box = document.querySelector('.box');

// .box observen
io.observe(box);

Dieses Beispiel, erweitert durch eine Statusanzeige, kann man sich hier ansehen. Der Code ist auf Github.

Mehrere Elemente beobachten

Möchte man mehrere Elemente beobachten, empfiehlt es sich, ein und den selben Observer dafür zu verwenden.

const io = new IntersectionObserver(entries => {
    console.log(entries);
});

// Elemente, die observed werden sollen
const box1 = document.querySelector('.box1');
const box2 = document.querySelector('.box2');

// .box1 und .box2 observen
io.observe(box1);
io.observe(box2);

Dieses Beispiel gibt es ebenfalls als Demo und den Code auf Github.

Kindelemente innerhalb eines anderen Elements observen

Man kann dem Observer Optionen übergeben. Beispielsweise kann man festlegen, dass Elemente nicht innerhalb des Dokuments beobachtet werden sollen, sondern innerhalb eines anderen Elements.

const io = new IntersectionObserver(entries => {

    // Verfügbare Daten, wenn das Element den sichtbaren Bereich des 
    // Elternelements betritt oder verlässt
    console.log(entries);
}, {

    // Festlegen welches Element das root-Element sein soll
    root: document.querySelector('.container')
});

// Element, das observed werden soll
const box = document.querySelector('.box');

// .box observen
io.observe(box);

Dieses Beispiel gibt es natürlich auch als Demo und den Code auf Github.

Checken wie viel von einem Element sichtbar ist

Eine weitere Option, die man übergeben kann ist threshold. Mit threshold kann man Werte festlegen, die definieren zu welchem Zeitpunkt der IO Callback aufgerufen werden soll. So bekommt man genauere Information darüber wie viel von dem Element schon sichtbar ist. Mit threshold: [0.25, 0.5, 0.75, 1] beispielsweise wird man jedesmal informiert, wenn ein zusätzliches Viertel des Elements sichtbar wird.

In diesem Beispiel sieht man threshold im Einsatz.

Infinite Scrolling

Mit IntersectionObserver lässt sich Infinite Scrolling sehr einfach umsetzen. Wichtig ist dabei nur, dass man nicht jedes einzelne Element in der Liste beobachtet, da das stark auf die Performance gehen kann.
Besser ist es ein leeres Element nach x Listelementen zu positionieren und dieses zu beobachten. Sobald es im Viewport ist, werden neue Listelemente geladen und das leere Element wird wieder an das Ende der Liste gesetzt.

HTML

<section class="articles">
     <article class="article">Article 1</article>
     <article class="article">Article 2</article>
     <article class="article">Article 3</article>
     <article class="article">Article 4</article>
     <article class="article">Article 5</article>
     <!-- Dieses Element wird beobachtet -->
     <div class="sentinel"></div>
     <article class="article">Article 6</article>
     <article class="article">Article 7</article>
     <article class="article">Article 8</article>
     <article class="article">Article 9</article>
     <article class="article">Article 10</article>
</section>

JS

// Indikator der beobachtet werden soll
const sentinel = document.querySelector('.sentinel');

// Artikel Container
const articles = document.querySelector('.articles');

// Artikelzähler
let counter = 15;

// Neue Artikel hinzufügen    
function addArticles(n) {
  for (var i = 0; i < n; i++) {
    var newArticle = document.createElement('article');
    newArticle.classList.add('article');
    counter++;
    newArticle.textContent = 'Article ' + counter;
    articles.appendChild(newArticle);
  }
}

// IntersectionObserver erstellen
const io = new IntersectionObserver(entries => {

  // Neue Artikel nur dann laden, wenn der Indikator in den 
  // Viewport kommt, nicht wenn er ihn verlässt.      
  if (entries[0].intersectionRatio <= 0) {
    return;
  }

  // Die nächsten 10 Artikel laden
  addArticles(10);

  // Indikator an das Ende der Liste schieben
  articles.appendChild(sentinel);

  // Die nächsten 5 Artikel laden      
  addArticles(5);

});

// Den Indikator .sentinel observen
io.observe(sentinel);

Demo und Code.

IO stoppen und deaktivieren

IntersectionObserver lassen sich auch wieder stoppen und deaktivieren.

// Element nicht mehr observen
io.unobserve(element);

// Gesamten IntersectionObserver deaktivieren
io.disconnect();

Browser Support

Aktuell funktionieren IntersectionObserver nur in Chrome 51+ und Chrome Canary, aber ein Polyfill ist gerade in der Entwicklung.

Quellen und weitere Ressourcen

Manuel Matuzovic

Frontend Developer aus Wien.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *