Blog Note technique

Notre image principale disparaissait sur iPhone : un bloqueur de publicités la cachait

Pendant des semaines, notre image principale a disparu sur iPhone sans que le laboratoire le reproduise. La cause : un bloqueur de publicités masquant toute classe commençant par ad-.

Le symptôme

Sur trois pages de ce site, le bloc principal (le titre et la photographie en haut de page) manquait quand la page se dessinait pour la première fois sur un iPhone. La section du dessous restait en haut de l'écran pendant environ une seconde et demie, puis le bloc apparaissait au-dessus. Rechargez la page et il était là instantanément. Ouvrez-la à neuf dans un onglet privé et il avait de nouveau disparu.

Cela n'arrivait que sur iPhone. Cela arrivait dans Safari et dans tous les autres navigateurs iOS, ce qui est logique puisqu'Apple les oblige tous à utiliser WebKit. Cela n'est jamais arrivé sur un ordinateur ni sur un téléphone Android.

Il nous a fallu des semaines pour le trouver. En chemin, nous avons livré plusieurs correctifs. Chacun partait d'une hypothèse raisonnable concernant le chargement des images, et aucun n'était la cause.

Les théories qui étaient fausses

Tout dans le symptôme désignait l'image : une photographie qui arrivait tard, sans espace réservé, et qui poussait le contenu vers le bas en se chargeant. Nous avons donc réservé l'espace avec une largeur, une hauteur et un rapport d'aspect. Nous avons préchargé la photographie depuis l'en-tête du document. Nous avons rempli l'espace réservé d'un substitut flouté pour qu'il ne soit jamais blanc. Chaque changement a rendu la page mesurablement meilleure, et aucun n'a touché au bogue.

Nous avons tenté de le reproduire dans Chromium avec le réseau bridé à une connexion mobile lente. Le bloc principal était mis en page dès la première image. Nous avons installé un vrai WebKit sur un serveur et retenu chaque image et chaque police deux secondes et demie. Même résultat. En laboratoire, la page allait bien.

Mesurer là où cela se produit

À ce stade, nous avons arrêté de deviner. Nous avons ajouté à la page un petit script temporaire, réservé aux téléphones, qui enregistrait où se trouvait réellement le bloc principal toutes les cent millisecondes pendant les cinq premières secondes, puis renvoyait une ligne à notre serveur. Le tout faisait moins de cinquante lignes.

Afficher le code Masquer le code JavaScript, 17 lignes
// Sample the hero's position for five seconds, then send one beacon.
const hero = document.querySelector("header.pagehead");
const samples = [];
const t0 = performance.now();
const tick = setInterval(() => {
  const r = hero.getBoundingClientRect();
  samples.push([
    Math.round(performance.now() - t0),
    Math.round(r.top), Math.round(r.height),
    getComputedStyle(hero).display,
    document.styleSheets.length,
  ]);
  if (samples.length >= 50) {
    clearInterval(tick);
    navigator.sendBeacon("/api/hero-trace.json", JSON.stringify(samples));
  }
}, 100);

Trois chargements de page depuis le téléphone qui montrait le problème, et la réponse était dans les chiffres.

Ce que le relevé disait

À 12 millisecondes après le démarrage du script, le bloc principal faisait 992 pixels de haut, juste sous la barre de navigation, avec deux feuilles de style sur la page. À 114 millisecondes, il faisait zéro pixel à la position zéro, son display calculé était none, et il y avait quatre feuilles de style sur la page. À 1 554 millisecondes, il était de retour.

Rien sur la page n'ajoute de feuilles de style. Aucune règle de notre CSS ne peut mettre cet élément en display none. Aucun script de la page n'y touche. Quelque chose d'extérieur à la page injectait donc deux feuilles de style au premier chargement, et l'une d'elles cachait notre bloc principal.

Le bloc principal de ces trois pages portait les classes ad-hero, ad-heroimg et ad-sub. Le préfixe abrégeait agencies et advocacy, deux des marchés que ces pages servent. Un bloqueur de publicités ne le sait pas.

Les filtres cosmétiques masquent les éléments dont le nom de classe commence par ad-, et les bloqueurs de contenu sur iOS injectent exactement ces règles sous forme de feuilles de style au premier chargement d'une page. La seconde passe, une seconde et demie plus tard, restaurait l'élément, ce qui explique qu'il finisse par apparaître. Au rechargement, les règles du bloqueur étaient déjà en place, il n'y avait donc rien à faire clignoter.

Le correctif

Renommer trois classes. Le bloc principal s'appelle désormais mk-hero, mk-heroimg et mk-sub sur les neuf pages qui les utilisaient, dans les trois langues, et une note en tête de la feuille de style empêche quiconque de nommer une classe ainsi à l'avenir. Le correctif était en ligne douze minutes après le retour du relevé.

Afficher le code Masquer le code CSS, 3 lignes
/* Class names: never start one with "ad", "ads", "advert", "sponsor",
   "promo", "banner" or "popup". Ad blockers' cosmetic filters hide
   elements by such prefixes. */

Ce qu'il faut vérifier sur votre propre site

Ouvrez le code source de n'importe quelle page et cherchez des valeurs de class= et d'id= qui commencent par ad, ads, advert, sponsor, promo, banner ou popup. Celles que nous voyons le plus sur les sites d'associations et d'organismes à but non lucratif sont ad-banner pour une barre d'annonce, sponsor-logos pour ceux qui ont payé la conférence, et promo pour la campagne d'adhésion. Une part significative de vos visiteurs ne voit aucun de ces éléments, et vous ne le remarquerez pas, parce que sur votre ordinateur tout s'affiche bien.

Si certains visiteurs signalent un bogue que vous ne parvenez pas à reproduire, ne livrez pas une théorie de plus. Posez une mesure sur la page pendant une journée. Elle est plus petite que le correctif, et elle vous montre ce qui se passe réellement.

Recevez le LFW Weekly Recap

Une lettre d’information envoyée chaque vendredi avec la semaine chez LFW. Un e-mail par semaine, et aucun autre article du blog.

Nous envoyons d'abord un e-mail de confirmation. Rien d'autre n'arrive avant votre clic.