vendredi 25 septembre 2026

À propos

Contact

Accessibilité

Un menu qui se ferme tout seul au clavier : un gestionnaire de clic mal placé

Impossible d'ouvrir le sous-menu au clavier sans qu'il ne se referme aussitôt. Le coupable : un écouteur d'événement posé au mauvais endroit du DOM.

Par Clément Hadrot • 20 avril 2022 • 4 min de lecture • Aucun commentaire
Un menu qui se ferme tout seul au clavier : un gestionnaire de clic mal placé

Un client nous a signalé un comportement agaçant sur le menu principal de son site vitrine : au clavier, en appuyant sur Entrée pour ouvrir un sous-menu, celui-ci s’affichait puis se refermait immédiatement, comme si une seconde touche invisible venait annuler l’action. À la souris, tout fonctionnait parfaitement. Le bug ne concernait donc que la navigation clavier, ce qui le rendait particulièrement difficile à reproduire pour quelqu’un qui teste toujours son site à la souris.

Ce billet détaille la démarche de diagnostic qui nous a menés jusqu’à la ligne de code fautive, puis la correction que nous avons appliquée pour que le menu reste ouvert le temps nécessaire à sa consultation.

Symptôme : une fermeture instantanée et silencieuse

En inspectant le comportement pas à pas avec les outils de développement du navigateur, nous avons observé que l’attribut aria-expanded du bouton passait bien de false à true pendant une fraction de seconde, avant de repasser immédiatement à false. Le sous-menu n’était donc pas bloqué : il s’ouvrait puis se refermait dans la même frappe.

Aucune erreur n’apparaissait dans la console. Le comportement semblait volontaire du point de vue du navigateur, ce qui nous a orientés vers un problème de logique applicative plutôt que vers un bug de rendu ou de CSS.

Diagnostic : un clic fantôme déclenché par le focus

Le thème utilisait un script maison pour fermer le menu lorsqu’on cliquait en dehors de lui, un pattern très courant :

document.addEventListener('click', function (event) {
  if (!menu.contains(event.target)) {
    menu.classList.remove('is-open');
    toggleButton.setAttribute('aria-expanded', 'false');
  }
});
L'essentiel à retenir : Le focus déclenchait un clic fantôme sur le document ; Un seul document.addEventListener mal ciblé suffisait ; La correction tient en une vérification de la cible de l'événement

Ce code semble raisonnable, mais il ignorait un détail : sur certains navigateurs, activer un bouton avec la touche Entrée déclenche un événement click synthétique juste après l’ouverture du menu, avec une cible d’événement qui, selon l’ordre d’exécution des scripts, pouvait être interprétée comme extérieure au conteneur du menu si celui-ci n’était pas encore repositionné dans le DOM au moment de l’évaluation. Le gestionnaire de fermeture s’exécutait donc dans la foulée du gestionnaire d’ouverture, sur le même événement clavier.

Correctif : distinguer l’origine de l’interaction

La solution retenue a consisté à ignorer l’événement de fermeture lorsqu’il provient du bouton d’ouverture lui-même, et à retarder l’écoute globale d’une frame pour laisser le temps au menu de se stabiliser dans le DOM avant d’activer la détection de clic extérieur :

toggleButton.addEventListener('click', function (event) {
  event.stopPropagation();
  const isOpen = menu.classList.toggle('is-open');
  toggleButton.setAttribute('aria-expanded', String(isOpen));
});

document.addEventListener('click', function (event) {
  if (event.target === toggleButton) {
    return;
  }
  if (!menu.contains(event.target)) {
    menu.classList.remove('is-open');
    toggleButton.setAttribute('aria-expanded', 'false');
  }
});

Le event.stopPropagation() posé sur le bouton d’ouverture empêche l’événement de remonter jusqu’à l’écouteur global du document pendant la même interaction. Une fois ce changement appliqué, le sous-menu est resté ouvert au clavier aussi longtemps qu’à la souris.

Vérification avec les technologies d’assistance

Nous avons rejoué le scénario avec NVDA sous Firefox et VoiceOver sous Safari. Dans les deux cas, l’ouverture au clavier restait stable, et l’attribut aria-expanded restait cohérent avec l’état visuel du menu, ce qui est essentiel : un lecteur d’écran annonce cet état à l’utilisateur, et un décalage entre les deux aurait continué à tromper les personnes non-voyantes même après correction visuelle.

  • Ouverture au clavier stable sur trois niveaux de sous-menu imbriqués
  • Fermeture correcte avec la touche Échap
  • Aucune régression constatée sur le comportement à la souris ou au tactile

Prévention pour les prochains menus

Ce type de bug se cache facilement parce qu’il ne se manifeste qu’au clavier, un mode d’interaction rarement testé en recette classique. Nous avons ajouté à notre check-list de recette un test systématique : ouvrir chaque menu déroulant au clavier, avec Tab puis Entrée, avant toute livraison. Nous recommandons aussi d’éviter les écouteurs globaux sur document quand un écouteur ciblé sur le conteneur du menu, combiné à event.stopPropagation(), suffit largement et réduit la surface de bugs de ce genre.

La leçon la plus utile de ce cas reste la suivante : un bug invisible à la souris n’est pas un bug mineur. Il est simplement invisible à la personne qui ne teste jamais autrement qu’à la souris, ce qui, statistiquement, exclut une partie non négligeable des visiteurs réels du site.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi