Un client signale un comportement étrange : sur son site, la touche Tab ne fait parfois « rien » en tout début de navigation, comme si le clavier était bloqué pendant une fraction de seconde. Après investigation, la cause est un widget de chat IA installé la semaine précédente, qui déplace le focus dans son champ de saisie dès son initialisation — sans qu’aucune action de l’utilisateur ne l’ait demandé.
Ce billet détaille comment ce bug a été identifié, pourquoi il est particulièrement pernicieux à détecter, et comment il a été corrigé sans désinstaller le widget.
Symptôme
À l’arrivée sur n’importe quelle page du site, un utilisateur qui commence à naviguer au clavier avec Tab se retrouve, après un court délai, projeté dans le champ de saisie du chatbot, en bas de page. S’il avait déjà commencé à lire le contenu principal ou à naviguer dans le menu, sa position est perdue sans avertissement.
Diagnostic

Le test au clavier immédiat après chargement ne révélait rien d’anormal : le focus restait sagement sur le premier lien de la page. C’est en laissant s’écouler quelques secondes avant de tester que le problème apparaissait. En inspectant le script du widget via les outils de développement du navigateur, la cause est apparue clairement : le widget attend le chargement complet de son modèle conversationnel côté client avant d’appeler element.focus() sur son champ de saisie, dans un délai d’environ 1,2 seconde après l’affichage de la page.
Ce délai est précisément ce qui rend le bug difficile à détecter en test rapide : un développeur pressé teste le Tab immédiatement après le rechargement de page, avant que le script asynchrone n’ait eu le temps de s’exécuter, et ne voit donc jamais le vol de focus se produire.
Pourquoi l’éditeur du widget a fait ce choix
En creusant la documentation du widget, l’intention devient compréhensible sans être excusable : l’éditeur voulait que l’utilisateur puisse taper immédiatement sa question dès l’arrivée sur le site, pour maximiser l’engagement avec l’assistant. Un objectif commercial légitime, mais implémenté sans considération pour la navigation clavier en cours.
Correctif
Le correctif retenu, en attendant une mise à jour de l’éditeur, consiste à intercepter l’appel de focus automatique via une surveillance de mutation du DOM, et à ne le laisser s’exécuter que si l’utilisateur a explicitement interagi avec le bouton d’ouverture du chat :
var champChat = document.querySelector('.widget-ia-input');
var chatOuvertParUtilisateur = false;
document.querySelector('.widget-ia-bouton').addEventListener('click', function () {
chatOuvertParUtilisateur = true;
});
if (champChat) {
var focusOriginal = champChat.focus.bind(champChat);
champChat.focus = function () {
if (chatOuvertParUtilisateur) {
focusOriginal();
}
};
}
Cette rustine reste temporaire : elle dépend d’une API interne du widget susceptible de changer à la prochaine mise à jour. Un ticket a été ouvert auprès de l’éditeur, avec une reproduction précise du bug et le délai mesuré.
Prévention
Pour éviter de découvrir ce type d’anomalie après mise en production, deux règles ont été ajoutées à la check-list de recette de tout nouveau script tiers :
- Tester la navigation au clavier en laissant s’écouler au moins cinq secondes après le chargement de la page, pas seulement immédiatement
- Surveiller les appels à
focus()dans le script tiers via les outils de développement, avant validation en recette
Un vol de focus retardé est plus dangereux qu’un vol de focus immédiat : il survient au moment où l’utilisateur a le moins de raisons de s’y attendre.
En résumé
Ce bug illustre une catégorie d’anomalies difficile à détecter : les scripts tiers qui interviennent sur le focus de façon asynchrone, en dehors de toute action utilisateur directe. Un test au clavier réalisé trop rapidement après chargement passe complètement à côté. La vigilance doit désormais porter autant sur le délai de test que sur le test lui-même.