La confiance est bonne, l'observation est préférable: Intersection Observer v2

Intersection Observer v2 permet non seulement d'observer les intersections en tant que telle, mais aussi détecter si l'élément concerné était visible au moment de l'intersection.

Intersection Observer v1 est l'une de ces API probablement appréciées par tous. Maintenant que Safari le prend aussi en charge. il est également utilisable universellement dans tous les principaux navigateurs. Pour vous rafraîchir la mémoire sur l'API, Je vous conseille de regarder Surma Micro-pointe surélevée sur une intersection Observer v1 intégré ci-dessous. Vous pouvez également lire le document détaillé de Surma . Intersection Observer v1 a été utilisé dans de nombreux cas d'utilisation, par exemple : le chargement différé d'images et de vidéos, être averti lorsque des éléments atteignent position: sticky ; déclencher des événements d'analyse, et bien d'autres encore.

Pour en savoir plus, consultez le Documentation d'Intersection Observer sur MDN Pour rappel, voici à quoi ressemble la version 1 de l'API Intersection Observer dans les cas de 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'));

Quels sont les défis liés à Intersection Observer v1 ?

En clair, Intersection Observer v1 est un outil formidable, mais il n'est pas parfait. Il y a dans certains cas particuliers où l'API n'est pas à la hauteur. Voyons cela de plus près. L'API Intersection Observer v1 peut vous indiquer quand l'utilisateur fait défiler un élément vers l'intérieur fenêtre d'affichage de la fenêtre, mais elle n'indique pas si l'élément est recouvert par tout autre contenu de la page (c'est-à-dire, lorsque l'élément est masqué) ou si l'affichage visuel de l'élément a été modifié par des effets visuels tels que transform, opacity, filter, etc., qui peuvent efficacement le rendre invisible.

Pour un élément du document de premier niveau, ces informations peuvent être déterminées en analysant le DOM via JavaScript, par exemple via DocumentOrShadowRoot.elementFromPoint() puis à creuser plus profondément. En revanche, il est impossible d'obtenir les mêmes informations si l'élément en question est situées dans un iFrame tiers.

Pourquoi la visibilité réelle est-elle si importante ?

Internet est malheureusement un endroit qui attire les acteurs malintentionnés aux pires intentions. Par exemple, un éditeur louche qui diffuse des annonces au paiement par clic sur un site de contenu peut être incité. pour inciter les internautes à cliquer sur leurs annonces afin d'augmenter les revenus publicitaires de l'éditeur (au moins pendant une courte période, jusqu'à ce que le réseau publicitaire les détecte). Généralement, ces annonces sont diffusées dans des cadres iFrame. Si l'éditeur souhaite que les internautes cliquent sur ce type d'annonce, il peut créer des cadres iFrame une transparence totale en appliquant une règle CSS iframe { opacity: 0; } et en superposant les cadres iFrame. par-dessus un élément attrayant, comme une vidéo de chat sur laquelle les utilisateurs voudraient cliquer. C'est ce qu'on appelle le détournement de clic. Vous pouvez voir une telle attaque de détournement de clic en action dans la section supérieure de ce demo (essayez de regarder la vidéo de chat) et activez le "mode astuce"). Vous remarquerez que l'annonce contenue dans l'iFrame "pense" la page a enregistré des clics légitimes, de façon transparente lorsque vous cliquez dessus (cela se fait involontairement).

Pour inciter un utilisateur à cliquer sur une annonce, appliquez un style transparent à celle-ci par-dessus un élément attrayant.

Comment Intersection Observer v2 résout-il ce problème ?

Intersection Observer v2 introduit le suivi de la "visibilité" réelle d'une cible en tant qu'être humain le définirait. Définissez une option dans le Constructeur IntersectionObserver intersections IntersectionObserverEntry instances contiendra alors un nouveau champ booléen nommé isVisible. Une valeur true pour isVisible est une bonne garantie de l'implémentation sous-jacente l'élément cible ne doit pas être masqué par d'autres contenus. et ne comporte aucun effet visuel qui modifierait ou déformer son affichage à l'écran. En revanche, une valeur false signifie que l'implémentation ne peut pas fournir cette garantie.

Un détail important de la caractéristiques est que l'implémentation est autorisée à signaler des faux négatifs (autrement dit, en définissant isVisible à false, même lorsque l'élément cible est entièrement visible et inchangé). Pour des raisons de performances ou pour d'autres raisons, les navigateurs se limitent à utiliser les limites. et la géométrie rectiligne, ils n'essaient pas d'obtenir des résultats au pixel près pour des modifications comme border-radius.

Toutefois, les faux positifs ne sont pas autorisés, quelles que soient les circonstances (par exemple, si vous définissez isVisible à true lorsque l'élément cible n'est pas entièrement visible et n'est pas modifié).

En pratique, à quoi ressemble le nouveau code ?

Le constructeur IntersectionObserver prend désormais deux propriétés de configuration supplémentaires: delay et trackVisibility. Le delay est un nombre indiquant le délai minimal en millisecondes entre les notifications des l'observateur pour une cible donnée. trackVisibility est une valeur booléenne indiquant si l'observateur va suivre les modifications de la stratégie d'enchères visibilité.

Il est important de noter ici que lorsque trackVisibility est défini sur true, delay doit être situé à 100 minimum (c'est-à-dire, pas plus d'une notification toutes les 100 ms). Comme indiqué précédemment, la visibilité est coûteuse à calculer, et cette exigence constitue une précaution dégradation des performances et de l'utilisation de la batterie. Le développeur responsable utilisera plus grande valeur tolérable pour le retard.

Selon la version actuelle spec, la visibilité est calculé comme suit:

  • Si l'attribut trackVisibility de l'observateur est false, la cible est considérée comme visible. Cela correspond au comportement actuel de la version 1.

  • Si la cible dispose d'une matrice de transformation efficace autre qu'une traduction en 2D ou 2D proportionnelle, la cible est considérée comme invisible.

  • Si la cible ou tout élément de la chaîne de blocs qui la contient a une opacité effective autre que 1.0, la cible est considérée comme invisible.

  • Si des filtres sont appliqués à la cible ou à tout élément de la chaîne de bloc qui la contient, alors la cible est considérée comme invisible.

  • Si l'implémentation ne peut pas garantir que la cible n'est pas complètement masquée par une autre page contenu, la cible est considérée comme invisible.

Cela signifie que les implémentations actuelles sont assez conservatrices et garantissent la visibilité. Par exemple, appliquer un filtre en nuances de gris presque invisible comme filter: grayscale(0.01%) ou définir une transparence presque invisible avec opacity: 0.99 affichera tous l'élément invisibles.

Vous trouverez ci-dessous un exemple de code abrégé qui illustre les nouvelles fonctionnalités de l'API. Vous pouvez voir le suivi des clics la logique en action dans la deuxième section de la démonstration (mais maintenant, essayez de "regarder" la vidéo du chiot). N'oubliez pas d'activer le "mode astuce" à nouveau pour transformez-vous en éditeur douteux et découvrez comment Intersection Observer v2 empêche les clics non légitimes sur les annonces. Cette fois-ci, Intersection Observer v2 est là pour vous ! 🎉

Intersection Observer v2 empêche les clics accidentels sur une annonce.

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

Remerciements

Merci à Simeon Vincent, Yoav Weiss et Mathias Bynens d'avoir consulté cet article, et aussi Stefan Zager. pour examiner et implémenter la fonctionnalité dans Chrome. Image héros de Sergey Semin sur Unsplash.