Zaufanie jest dobre, obserwacja jest lepsza: obserwacja Intersection Observer (wersja 2)

Intersection Observer v2 umożliwia nie tylko obserwowanie skrzyżowań, ale również wykryć, czy w chwili skrzyżowania przecinający się element był widoczny.

Intersection Observer v1 to jeden z najpopularniejszych interfejsów API, Safari obsługuje też tę przeglądarkę, ale w końcu możesz go używać we wszystkich popularnych przeglądarkach. Aby przypomnieć sobie, jak działa interfejs API, Zachęcam do obejrzenia kanału Surma Doładowana miniatura na przecięciu Umieszczony poniżej Observer v1. Możesz też przeczytać szczegółowy opis serwisu Surma artykuł. Użytkownicy używali Intersection Observer v1 w wielu zastosowaniach, leniwe ładowanie obrazów i filmów, otrzymywanie powiadomień, gdy pierwiastki osiągają position: sticky, uruchamianie zdarzeń analitycznych, i wiele innych.

Aby dowiedzieć się więcej, zobacz Dokumentacja Intersection Observer w MDN Tak wygląda interfejs Intersection Observer v1 API podstawowy przypadek:

const onIntersection = (entries) => {
  for (const entry of entries) {
    if (entry.isIntersecting) {
      console.log(entry);
    }
  }
};

const observer = new IntersectionObserver(onIntersection);
observer.observe(document.querySelector('#some-target'));

Co stanowi wyzwanie w aplikacji Intersection Observer v1?

Dla jasności – wersja 1 Intersection Observer jest świetna, ale ta wersja nie jest jeszcze idealna. Istnieją w niektórych przypadkach, gdy interfejs API nie spełnia wymagań. Przyjrzyjmy się temu bliżej. Interfejs Intersection Observer v1 API poinformuje Cię, gdy element przewinie się do widoczny obszar okna, ale nie informuje o tym, czy element jest zasłonięty. przez jakąkolwiek inną treść strony (czyli gdy element jest ukryty) lub wygląd elementu został zmodyfikowany przez efekty wizualne, takie jak transform, opacity, filter itp., dzięki czemu efektywnie jest on niewidoczny.

W przypadku elementu dokumentu najwyższego poziomu informacje te można określić, analizując DOM za pomocą JavaScriptu, np. przez DocumentOrShadowRoot.elementFromPoint() a następnie głębiej. Nie można natomiast uzyskać tych samych informacji, jeśli dany element jest znajduje się w elemencie iframe innej firmy.

Dlaczego faktyczna widoczność jest tak ważna?

Internet jest niestety miejscem, które przyciąga nieuczciwych użytkowników o gorszych intencjach. Na przykład nieuczciwy wydawca, który wyświetla w witrynie z treścią reklamy płatne za kliknięcie, może otrzymywać zachęty podstępem nakłaniają użytkowników do klikania reklam i w ten sposób zwiększyć zarobki z reklam (przynajmniej przez krótki czas, aż zostaną wychwycone przez sieć reklamową). Zazwyczaj tego typu reklamy wyświetlają się w elementach iframe. Jeśli wydawca chce zachęcić użytkowników do klikania takich reklam, może utworzyć elementy iframe reklam całkowicie przezroczyste przez zastosowanie reguły CSS iframe { opacity: 0; } i nałożenie elementów iframe. do czegoś atrakcyjnego, na przykład filmu ze słodkim kotkiem, który użytkownicy będą chcieli kliknąć. Jest to tzw. clickjacking. Taki atak typu clickjacking można zobaczyć w górnej sekcji demo (spróbuj „obejrzeć” film z kotem i aktywuj „tryb nieuczciwego”). Zauważysz, że reklama w elemencie iframe „myśli”, uzyskał prawidłowe kliknięcia, nawet jeśli w sposób niezauważalny dla użytkownika, który go kliknął.

Treści nieuczciwe nakłaniające użytkownika do kliknięcia reklamy przez nałożenie jej na coś atrakcyjnego.

Jak rozwiązać ten problem z narzędziem Intersection Observer w wersji 2?

Intersection Observer v2 wprowadza sposób śledzenia rzeczywistej „widoczności”. wartości docelowej co człowiek określiłby go. Ustawiając opcję w konstruktora IntersectionObserver, przecinające się IntersectionObserverEntry instancje będą zawierać nowe pole wartości logicznej o nazwie isVisible. Wartość true w polu isVisible stanowi silną gwarancję wynikającą z implementacji że element docelowy jest całkowicie wyłączony z innych treści. i nie zawiera efektów wizualnych, które mogłyby zniekształcić lub zniekształcić obraz. Wartość false oznacza natomiast, że implementacja nie gwarantuje tej gwarancji.

Ważnym elementem specyfikacja jest to, że implementacja może zgłaszać fałszywie negatywne (tzn. ustawienie isVisible) do false nawet wtedy, gdy element docelowy jest w pełni widoczny i niezmodyfikowany). Ze względu na wydajność lub z innych powodów przeglądarki ograniczają się do pracy z ograniczeniami prostokąty i geometria prostoliniowa; ale nie starają się osiągnąć idealnych wyników modyfikacje takie jak border-radius.

Jednak fałszywie trafienia nie są dozwolone w żadnym przypadku (tzn. w przypadku ustawienia isVisible do true, gdy element docelowy nie jest w pełni widoczny i niezmodyfikowany).

Jak nowy kod wygląda w praktyce?

Konstruktor IntersectionObserver przyjmuje teraz 2 dodatkowe właściwości konfiguracji: delay i trackVisibility. Wartość delay to liczba wskazująca minimalne opóźnienie (w milisekundach) między powiadomieniami od obserwatora danej planety. trackVisibility to wartość logiczna wskazująca, czy obserwator będzie śledzić zmiany w widoczność.

Pamiętaj, że gdy trackVisibility ma wartość true, delay musi znajdować się w co najmniej 100 (czyli nie więcej niż jedno powiadomienie co 100 ms). Jak już wspomnieliśmy, widoczność jest kosztowna, a wymaganie to jest środkiem ostrożności, pogorszenie wydajności i zużycie baterii. Odpowiedzialny deweloper będzie używać największą dopuszczalną wartość opóźnienia.

Zgodnie z obecnymi zasadami spec, widoczność to oblicza się w ten sposób:

  • Jeśli atrybut trackVisibility obserwatora ma wartość false, cel jest uznawany za widoczny. Odpowiada to obecnemu zachowaniu w wersji 1.

  • Jeśli obiekt docelowy ma efektywną macierz przekształceń inną niż tłumaczenie 2D lub proporcjonalne skalowanie 2D, cel jest uznawany za niewidoczny.

  • Jeśli element docelowy lub dowolny element w jego łańcuchu bloków ma efektywną przezroczystość inną niż 1,0, cel jest uznawany za niewidoczny.

  • jeśli cel lub dowolny element w jego łańcuchu bloków ma zastosowane jakieś filtry, cel jest uważany za niewidoczny.

  • Jeśli implementacja nie może zagwarantować, że miejsce docelowe jest całkowicie wykluczone przez inną stronę cel jest uważany za niewidoczny.

Oznacza to, że obecne implementacje są dość zachowawcze i zapewniają widoczność. Na przykład zastosowanie prawie niezauważalnego filtra skali szarości, takiego jak filter: grayscale(0.01%) lub ustawienie prawie niewidocznej przezroczystości za pomocą parametru opacity: 0.99 spowoduje wyrenderowanie elementu niewidoczny.

Poniżej znajduje się krótki przykładowy kod ilustrujący nowe funkcje interfejsu API. Dane śledzenia kliknięć są dostępne jak działa logika w drugiej części prezentacji. (ale teraz „obejrzyj” film ze szczeniakiem). Pamiętaj, aby włączyć „tryb sztuczki” ponownie, aby natychmiast zmień się w nieciekawego wydawcę i przekonaj się, jak pozwala to kontrolować Intersection Observer v2 nieuprawnionych kliknięć reklam. Tym razem wracamy do Intersection Observer v2. 🎉

Obserwatorzy skrzyżowań w wersji 2, który zapobiega niezamierzonemu kliknięciu reklamy.

<!DOCTYPE html>
<!-- This is the ad running in the iframe -->
<button id="callToActionButton">Buy now!</button>
// This is code running in the iframe.

// The iframe must be visible for at least 800ms prior to an input event
// for the input event to be considered valid.
const minimumVisibleDuration = 800;

// Keep track of when the button transitioned to a visible state.
let visibleSince = 0;

const button = document.querySelector('#callToActionButton');
button.addEventListener('click', (event) => {
  if ((visibleSince > 0) &&
      (performance.now() - visibleSince >= minimumVisibleDuration)) {
    trackAdClick();
  } else {
    rejectAdClick();
  }
});

const observer = new IntersectionObserver((changes) => {
  for (const change of changes) {
    // ⚠️ Feature detection
    if (typeof change.isVisible === 'undefined') {
      // The browser doesn't support Intersection Observer v2, falling back to v1 behavior.
      change.isVisible = true;
    }
    if (change.isIntersecting && change.isVisible) {
      visibleSince = change.time;
    } else {
      visibleSince = 0;
    }
  }
}, {
  threshold: [1.0],
  // 🆕 Track the actual visibility of the element
  trackVisibility: true,
  // 🆕 Set a minimum delay between notifications
  delay: 100
}));

// Require that the entire iframe be visible.
observer.observe(document.querySelector('#ad'));

Podziękowania

Dzięki Simeonowi Vincentowi, Yoav Weiss i Mathias Bynens za przeczytanie tego artykułu, a także Stefana Zagera. za zapoznanie się z nią i wdrożenie jej w Chrome. Baner powitalny: Unsplash: Sergey Semin.