Einige funktionale Anwendungsfälle für Intersection Observer, um zu wissen, wann ein Element im Sichtbereich ist

Avatar of Preethi
Preethi am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Sie wissen es vielleicht nicht, aber JavaScript hat in letzter Zeit einige Beobachter heimlich angehäuft, und der Intersection Observer ist Teil dieses Arsenals. Observer sind Objekte, die in Echtzeit etwas erkennen – wie Vogelbeobachter, die zu ihrem Lieblingsplatz gehen, um auf Vögel zu warten.

Unterschiedliche Observer beobachten unterschiedliche Dinge (nicht jeder beobachtet Habichte).

Der allererste Observer, den ich kennengelernt habe, war der Mutation Observer, der nach Änderungen im DOM-Baum sucht. Damals war er einzigartig, aber jetzt haben wir viele weitere Observer.

Intersection Observer beobachtet die „Schnittmenge“ (d. h. das Vorbeiziehen) eines Elements durch eines seiner Elternelemente oder den Bereich auf dem Bildschirm, wo die Seite sichtbar ist (auch als Viewport bekannt).

Es ist ein bisschen so, als würde man einen Zug durch einen Bahnhof fahren sehen. Man kann sehen, wann der Zug einfährt, wann er abfährt und wie lange er stillstand.

Zu wissen, wann ein Element im Begriff ist, in den Sichtbereich zu gelangen, ob es aus dem Sichtbereich verschwunden ist oder wie lange es her ist, seit es in den Sichtbereich gelangt ist, hat alles nützliche Anwendungen. Wir werden also einige dieser Anwendungsfälle jetzt sehen – direkt nachdem wir den Code zum Erstellen eines IntersectionObserver-Objekts über die Intersection Observer API gesehen haben.

Ein kurzer Überblick über IntersectionObserver

Die Intersection Observer API hat zum Zeitpunkt des Schreibens bereits breite Unterstützung erfahren.

Diese Daten zur Browserunterstützung stammen von Caniuse, wo es weitere Details gibt. Eine Zahl bedeutet, dass der Browser die Funktion ab dieser Version unterstützt.

Desktop

ChromeFirefoxIEEdgeSafari
5855Nein1612.1

Mobil / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
12712712712.2-12.5

Wenn Sie jedoch prüfen möchten, ob Intersection Observer unterstützt wird, während Sie damit arbeiten, können Sie sehen, ob die Eigenschaft IntersectionObserver im window-Objekt vorhanden ist.

if(!!window.IntersectionObserver){}
/* or */
if('IntersectionObserver' in window){}

Okay, nun werfen wir einen Blick auf die Objekterstellung.

var observer = new IntersectionObserver(callback, options);

Der Konstruktor des IntersectionObserver-Objekts nimmt zwei Parameter entgegen. Der erste ist eine Callback-Funktion, die ausgeführt wird, sobald der Observer eine Schnittmenge bemerkt und asynchron einige Daten über diese Schnittmenge geliefert hat.

Der zweite (optionale) Parameter sind Optionen, ein Objekt mit Informationen zur Definition dessen, was die „Schnittmenge“ sein wird. Wir wollen vielleicht nicht wissen, wann ein Element im Begriff ist, in den Sichtbereich zu gelangen, sondern nur, wenn es *vollständig* sichtbar ist. So etwas wird über den Optionsparameter definiert.

Optionen hat drei Eigenschaften.

  • root – Das Elternelement/Viewport, mit dem das beobachtete Element eine Schnittmenge bildet. Stellen Sie sich das als den Bahnhof vor, mit dem der Zug eine Schnittmenge bildet.
  • rootMargin – Ein Rand des Root-Elements, der den Bereich des Root-Elements verkleinert oder vergrößert, um nach Schnittmengen Ausschau zu halten. Ähnlich der CSS-Eigenschaft margin.
  • threshold – Ein Array von Werten (zwischen 0 und 1,0), die jeweils den Abstand angeben, den ein Element in den Root hineinragt oder darüber hinausgeht, bei dem der Callback ausgelöst werden soll.

Nehmen wir an, unser Schwellenwert ist 0,5. Der Callback wird ausgelöst, wenn das Element seinen halb sichtbaren Schwellenwert erreicht oder überschreitet. Wenn der Wert [0,3, 0,6] ist, wird der Callback ausgelöst, wenn das Element seinen 30% sichtbaren Schwellenwert erreicht oder überschreitet und auch seinen 60% sichtbaren Schwellenwert.

Genug Theorie für jetzt. Sehen wir uns einige Demos an. Zuerst das Lazy Loading.

Anwendungsfall 1: Lazy Loading von Bildern

Sehen Sie den Pen
Intersection Observer – Lazy Load
von Preethi Sam (@rpsthecoder)
auf CodePen.

Um das Laden „auf den Punkt“ zu sehen, besuchen Sie diese Webseite, da die eingebettete Demo dies nicht zeigt.

CSS-Tricks hat Lazy Loading bereits gründlich behandelt, und normalerweise wird es so gemacht: Ein leichtgewichtiger Platzhalter wird dort angezeigt, wo Bilder sein sollen, und dann werden sie durch die beabsichtigten Bilder ersetzt, wenn sie in den Sichtbereich gelangen (oder kurz davor sind). Glauben Sie mir, es ist nichts Lazy an der Implementierung – bis wir etwas Natives zum Arbeiten haben.

Wir wenden die gleiche Mechanik an. Zuerst haben wir eine Reihe von Bildern und einen Platzhalterbild definiert, das anfänglich angezeigt wird. Wir verwenden ein Datenattribut, das die URL des ursprünglichen Bildes enthält, das angezeigt werden soll, und das das tatsächliche Bild definiert, das geladen wird, wenn es in den Sichtbereich gelangt.

<img src="placeholder.png" data-src="img-1.jpg">
<img src="placeholder.png" data-src="img-2.jpg">
<img src="placeholder.png" data-src="img-3.jpg">
<!-- more images -->

Der Rest ist Skripting.

let observer = new IntersectionObserver(
(entries, observer) => { 
  entries.forEach(entry => {
    /* Here's where we deal with every intersection */
  });
}, 
{rootMargin: "0px 0px -200px 0px"});

Die obige Callback-Funktion ist eine Pfeilfunktion (Sie können aber auch eine normale Funktion verwenden).

Die Callback-Funktion empfängt zwei Parameter: eine Reihe von Einträgen (entries) mit Informationen über jede Schnittmenge und den Observer selbst. Diese Einträge können gefiltert oder durchlaufen werden, damit wir uns mit den gewünschten Schnittmengen befassen können. Was die Optionen betrifft, so habe ich nur den Wert rootMargin angegeben und die Eigenschaften root und threshold auf ihre Standardwerte fallen gelassen.

Der Standardwert für root ist der Viewport und der Standardwert für threshold ist 0 – was grob übersetzt werden kann als „Benachrichtige mich, sobald das Element im Viewport erscheint!“

Das Seltsame ist jedoch, dass ich den Beobachtungsbereich des Viewports um zweihundert Pixel am unteren Rand mit rootMargin reduziert habe. Normalerweise würden wir das beim Lazy Loading nicht tun. Stattdessen könnten wir den Rand vergrößern oder ihn auf dem Standardwert belassen. Aber eine Verringerung ist in dieser Situation nicht üblich. Ich habe es nur getan, weil ich zeigen möchte, wie die Originalbilder am Schwellenwert im beobachteten Bereich geladen werden. Andernfalls würde die gesamte Aktion außerhalb des Sichtbereichs stattfinden.

Wenn das Bild mit dem Beobachtungsbereich des Viewports (der 200 Pixel von unten im Demo ist) eine Schnittmenge bildet, ersetzen wir das Platzhalterbild durch das tatsächliche Bild.

let observer = new IntersectionObserver(
(entries, observer) => { 
entries.forEach(entry => {
    /* Placeholder replacement */
    entry.target.src = entry.target.dataset.src;
    observer.unobserve(entry.target);
  });
}, 
{rootMargin: "0px 0px -200px 0px"});

entry.target ist das Element, das vom Observer beobachtet wird. In unserem Fall sind das die Bildelemente. Sobald der Platzhalter in einem Bildelement ersetzt wurde, müssen wir es nicht mehr beobachten, also rufen wir die Methode unobserve des Observers für dieses Element auf.

Jetzt, da der Observer bereit ist, ist es Zeit, alle Bilder mit der Methode observer zu beobachten.

document.querySelectorAll('img').forEach(img => { observer.observe(img) });

Das war's! Wir haben die Bilder lazy geladen. Weiter geht's mit der nächsten Demo.

Anwendungsfall 2: Video automatisch pausieren, wenn es aus dem Sichtbereich verschwindet

Nehmen wir an, wir schauen uns ein Video auf YouTube an und wollen (aus welchem Grund auch immer) nach unten scrollen, um die Kommentare zu lesen. Ich weiß nicht, wie es Ihnen geht, aber normalerweise pausiere ich das Video nicht, bevor ich das tue, was bedeutet, dass ich einen Teil des Videos verpasse, während ich surfe.

Wäre es nicht schön, wenn das Video für uns pausieren würde, wenn wir wegscrollen? Es wäre noch schöner, wenn das Video wieder abspielen würde, wenn es wieder im Sichtbereich ist, sodass man nicht einmal auf Play oder Pause drücken muss.

Intersection Observer kann das auf jeden Fall.

Sehen Sie den Pen
IntersectionObserver: Video außerhalb des Sichtbereichs automatisch pausieren
von Preethi Sam (@rpsthecoder)
auf CodePen.

Hier ist unser Video im HTML.

<video src="OSRO-animation.mp4" controls=""></video>

So schalten wir zwischen Wiedergabe und Pause um.

let video = document.querySelector('video');
let isPaused = false; /* Flag for auto-paused video */
let observer = new IntersectionObserver((entries, observer) => { 
  entries.forEach(entry => {
    if(entry.intersectionRatio!=1  && !video.paused){
      video.pause(); isPaused = true;
    }
    else if(isPaused) {video.play(); isPaused=false}
  });
}, {threshold: 1});
observer.observe(video);

Bevor ich Ihnen zeige, wie wir das Video bei jeder Schnittmenge (d. h. jedem Eintrag) pausieren und abspielen, möchte ich Ihre Aufmerksamkeit auf die Eigenschaft threshold der Optionen lenken.

Der threshold hat den Wert 1. Sowohl root als auch rootMargin werden standardmäßig verwendet. Das bedeutet, „Hey, lass mich wissen, sobald das Element *vollständig* im Viewport sichtbar ist.“

Sobald die Schnittmenge eintritt und der Callback ausgelöst wird, pausieren oder spielen wir das Video basierend auf dieser Logik.

A flow chart for toggling play and pause on a video, where if video not fully visible and not paused, then isPaused is true. But if video was auto-paused, then IsPaused is false.

Ich habe unobserve für das Video nicht aufgerufen, daher beobachtet der Observer das Video weiterhin und pausiert *jedes Mal*, wenn es aus dem Sichtbereich verschwindet.

Anwendungsfall 3: Sehen, wie viel Inhalt angesehen wurde

Dies kann auf viele Arten interpretiert und implementiert werden, abhängig davon, was Ihre Inhalte sind und wie Sie messen möchten, wie viel davon angesehen wurde.

Als einfaches Beispiel beobachten wir den letzten Absatz jedes Artikels in einer Liste von Artikeln auf einer Seite. Sobald der letzte Absatz eines Artikels vollständig sichtbar wird, betrachten wir diesen Artikel als gelesen – ähnlich wie wir sagen könnten, dass das Sehen des letzten Waggons eines Zuges bedeutet, den ganzen Zug gesehen zu haben.

Hier ist ein Demo, das zwei Artikel auf einer Seite zeigt, die jeweils eine Reihe von Absätzen enthalten.

Sehen Sie den Pen
IntersectionObserver: Inhalt angesehen
von Preethi Sam (@rpsthecoder)
auf CodePen.

Unser vereinfachtes HTML sieht etwa so aus.

<div id="count"><!-- The place where "number of articles viewed" is displayed --></div>

<h2>Article 1</h2>
<article>
  <p><!-- Content --></p>
  <!-- More paragraphs -->
</article>
<h2>Article 2</h2>
<article>
  <p><!-- Content --></p>
  <!-- More paragraphs -->
</article>
<!-- And so on... -->
let n=0; /* Total number of articles viewed */
let count = document.querySelector('#count');
let observer = new IntersectionObserver((entries, observer) => { 
  entries.forEach(entry => {
    if(entry.isIntersecting){
      count.textContent= `articles fully viewed - ${++n}`; 
      observer.unobserve(entry.target);
    }
  });
}, {threshold: 1});

document.querySelectorAll('article > p:last-child').forEach(p => { observer.observe(p) });

Bei jeder Schnittmenge – der vollständigen Ansicht des letzten Absatzes eines Artikels – erhöhen wir einen Zähler: n, der die Gesamtzahl der gelesenen Artikel darstellt. Dann zeigen wir diese Zahl über der Liste der Artikel an.

Sobald wir eine Schnittmenge des letzten Absatzes gezählt haben, muss dieser nicht mehr beobachtet werden, also rufen wir unobserve dafür auf.

Danke fürs Zuschauen!

Das war's für die Beispiele, die wir uns gemeinsam in diesem Beitrag ansehen werden. Sie haben wahrscheinlich die Idee, wie es funktioniert, Elemente zu beobachten und Ereignisse auszulösen, basierend darauf, wo sie mit dem Viewport in Schnittmenge treten.

Das gesagt, ist es ratsam, vorsichtig zu sein, wenn visuelle Änderungen auf der Grundlage der durch den Observer erhaltenen Schnittmengen-Daten vorgenommen werden. Sicher, Intersection Observer ist problemlos, wenn es darum geht, Schnittmengen-Daten zu *protokollieren*. Aber wenn er verwendet wird, um Änderungen auf dem Bildschirm vorzunehmen, müssen wir sicherstellen, dass die Änderungen nicht verzögert sind, was möglich ist, da wir im Grunde genommen Änderungen auf der Grundlage von Daten vornehmen, die *asynchron* abgerufen werden. Das kann etwas Zeit zum Laden benötigen.

Wie wir gesehen haben, hat jeder Schnittmengen-Eintrag eine Reihe von Eigenschaften, die Informationen über die Schnittmenge vermitteln. Ich habe nicht alle davon in diesem Beitrag behandelt, also lesen Sie sie sich unbedingt durch.