La fiducia è buona, l'osservazione è meglio: Intersection Exampler v2

Intersection Observer v2 aggiunge la capacità non solo di osservare le intersezioni in sé, ma anche di rileva se l'elemento che si interseca era visibile al momento dell'intersezione.

Intersection Observer v1 è una di quelle API probabilmente amati da tutti e ora che È supportato anche da Safari, è finalmente utilizzabile universalmente in tutti i principali browser. Per un rapido ripasso dell'API, ti consiglio di guardare Surma Micropunta super ricaricata sull'intersezione Osservatore v1 incorporato di seguito. Puoi anche leggere gli approfondimenti di Surma articolo. Gli utenti hanno utilizzato Intersection Observer v1 per un'ampia gamma di casi d'uso, come caricamento lento di immagini e video, ricevere notifiche quando gli elementi raggiungono position: sticky, di attivare eventi di analisi, e molto altro ancora.

Per i dettagli completi, consulta Documentazione di Intersection Observer su MDN ma ti ricordiamo che questo è l'aspetto dell'API Intersection Observer v1 nella maggior parte dei casi. richiesta di base:

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'));

Quali problemi sono presenti nella versione 1 di Intersection Observer?

Precisiamo che Intersection Observer v1 è ottimo, ma non perfetto. Esistono i casi in cui l'API non è all'altezza. Diamo un'occhiata più da vicino. L'API Intersection Observer v1 può indicare quando un elemento viene fatto scorrere verso il l'area visibile della finestra, ma non indica se l'elemento è coperto da qualsiasi altro contenuto della pagina (vale a dire, quando l'elemento è occulto) o dal fatto che la visualizzazione visiva dell'elemento è stata modificata da effetti visivi come transform, opacity, filter e così via, che possono renderli efficaci visibili.

Per un elemento del documento di primo livello, queste informazioni possono essere determinate analizzando il DOM tramite JavaScript, ad esempio DocumentOrShadowRoot.elementFromPoint() per poi andare più a fondo. Al contrario, le stesse informazioni non possono essere ottenute se l'elemento in questione è in un iframe di terze parti.

Perché la visibilità effettiva è così importante?

Sfortunatamente, Internet è un luogo che attira malintenzionati con intenzioni peggiori. Ad esempio, un publisher losco che pubblica annunci pay-per-click su un sito di contenuti potrebbe essere incentivato per indurre con l'inganno le persone a fare clic sui propri annunci e aumentare il pagamento dell'annuncio al publisher (almeno per un breve periodo di tempo, fino a quando la rete pubblicitaria non li individua). Generalmente, questi annunci vengono pubblicati in iframe. Se il publisher volesse convincere gli utenti a fare clic su tali annunci, potrebbe creare iframe assolutamente trasparente, applicando una regola CSS iframe { opacity: 0; } e sovrapponendo gli iframe su qualcosa di accattivante, ad esempio un grazioso video di gatti su cui gli utenti vorrebbero davvero fare clic. Questa pratica è chiamata clickjacking. Puoi vedere un attacco clickjacking in azione nella sezione superiore di questo demo (prova a "guardare" il video dei gatti) e attivare la "modalità trucco"). Noterai che l'annuncio nell'iframe "pensa" ha ricevuto clic legittimi, anche se in modo completamente trasparente quando gli utenti fanno (fingete involontariamente) di fare clic su di essi.

Indurre l'utente a fare clic su un annuncio assegnandogli uno stile trasparente e sovrapponendolo a un elemento interessante.

In che modo può risolvere il problema tramite Intersection Observer v2?

Intersection Observer v2 introduce il concetto di monitoraggio della "visibilità" effettiva di un target elemento definito da un essere umano. Impostando un'opzione nella sezione costruttore IntersectionObserver, intersezione IntersectionObserverEntry conterrà un nuovo campo booleano denominato isVisible. Un valore true di isVisible è una solida garanzia dell'implementazione sottostante che l'elemento target sia completamente libero da altri contenuti. e non vengono applicati effetti visivi che alterano o distorceno la visualizzazione sullo schermo. Al contrario, un valore false significa che l'implementazione non può garantire questa garanzia.

Un dettaglio importante specifiche è che l'implementazione è consentita a segnalare falsi negativi (ovvero, l'impostazione di isVisible a false anche quando l'elemento target è completamente visibile e non modificato). Per motivi di prestazioni o per altri motivi, i browser si limitano a lavorare con i limiti scatole e geometria rettilinea; per cui non cercano di ottenere risultati perfetti modifiche come border-radius.

Detto ciò, i falsi positivi non sono consentiti in alcuna circostanza (ovvero, quando si imposta isVisible a true quando l'elemento target non è completamente visibile e non è stato modificato).

Come si presenta il nuovo codice in pratica?

Il costruttore IntersectionObserver ora richiede due proprietà di configurazione aggiuntive: delay e trackVisibility. Il delay è un numero che indica il ritardo minimo in millisecondi tra le notifiche da l'osservatore per un dato bersaglio. trackVisibility è un valore booleano che indica se l'osservatore terrà traccia delle variazioni nel visibilità.

È importante notare qui che quando trackVisibility è true, è necessario che delay si trovi a almeno 100 (ovvero non più di una notifica ogni 100 ms). Come osservato in precedenza, il calcolo della visibilità è costoso e questo requisito è una precauzione contro delle prestazioni e il consumo della batteria. Lo sviluppatore responsabile utilizzerà il maggiore valore tollerabile per il ritardo.

In base all'attuale spec, la visibilità è calcolato come segue:

  • Se l'attributo trackVisibility dell'osservatore è false, il target è considerato visibile. Corrisponde al comportamento attuale della versione 1.

  • Se il target ha una matrice di trasformazione efficace diversa da una traduzione 2D o 2D proporzionale, il target è considerato invisibile.

  • Se il target, o qualsiasi elemento nella catena di blocchi che la contiene, ha un'opacità effettiva diversa da 1,0, il target è considerato invisibile.

  • Se al target, o a qualsiasi elemento nella catena di blocchi che la contiene, sono applicati filtri, il target è considerato invisibile.

  • Se l'implementazione non può garantire che il target sia completamente ignorato da un'altra pagina contenuti, il target è considerato invisibile.

Ciò significa che le implementazioni attuali sono piuttosto prudenti e garantiscono la visibilità. Ad esempio, l'applicazione di un filtro scala di grigi quasi impercettibile come filter: grayscale(0.01%) oppure l'impostazione di una trasparenza quasi invisibile con opacity: 0.99 viene eseguito il rendering dell'elemento invisibile.

Di seguito è riportato un breve esempio di codice che illustra le nuove funzionalità dell'API. Puoi vedere il monitoraggio dei clic la logica in azione nella seconda sezione della demo (ma ora prova a "guardare" il video del cucciolo). Non dimenticare di attivare la "modalità trucco" da nuovo a immediatamente convertiti in un publisher losco e scopri come Intersection Observer v2 impedisce il tracciamento di clic sugli annunci non legittimi. Questa volta ci pensa Intersection Observer v2! 🎉

Intersection Observer v2 impedisce il clic involontario su un annuncio.

<!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'));

Ringraziamenti

Grazie a Simeon Vincent, Yoav Weiss e Mathias Bynens per leggere questo articolo e anche Stefan Zager per esaminare e implementare la funzionalità in Chrome. Immagine hero di Sergey Semin su Unsplash.