Pourquoi mon menu burger ne s'ouvre-t-il qu'au deuxième clic ?
En une phrase : si vous avez activé un délai JavaScript (chargement des scripts au premier clic ou après un minuteur), le tout premier tap sur le burger réveille le chargement du script, il n'ouvre pas le menu. Le menu ne répond qu'au deuxième tap. La solution : isoler le script du menu et ses dépendances du délai, ou mieux, lui donner un menu vanilla sans aucune dépendance.
Vous appuyez sur le burger : rien. Vous appuyez à nouveau : le menu s'ouvre. À chaque fois, il faut deux taps. Ce n'est ni aléatoire ni un bug de thème : c'est le comportement mécanique du « JavaScript delay », une optimisation très répandue. Voici pourquoi, et les deux façons d'y remédier.
Ce que fait le « JavaScript delay » (et pourquoi le premier clic est perdu)
Pour gagner des points de performance, le « JS delay » (ou « chargement à l'interaction ») reporte l'exécution des scripts jusqu'à ce que le visiteur fasse quelque chose : un scroll, un mouvement de souris, ou un premier tap. Tant que personne ne bouge, aucun JavaScript ne s'exécute, et le score initial grimpe.
Le problème saute aux yeux dès qu'on y réfléchit : ce tout premier tap sur le burger EST cette première interaction. Ce tap n'ouvre donc pas le menu, il déclenche le chargement des scripts. Le temps qu'ils arrivent et s'attachent au bouton, votre doigt est déjà reparti. Le menu ne s'ouvre qu'au deuxième tap, quand le script est enfin là et à l'écoute.
Tap 1 → rien
Le tap est « consommé » pour lancer le chargement du script, pas pour ouvrir le menu.
Tap 2 → le menu s'ouvre
Les scripts ont enfin chargé et le gestionnaire est désormais à l'écoute sur le bouton.
C'est encore plus visible avec une variante à minuteur : si les scripts se chargent « après 3 secondes », un visiteur qui tape avant se retrouve face à un burger mort.
Confirmer que c'est bien ça (30 secondes)
Trois vérifications simples :
- Le symptôme est constant, pas intermittent : c'est toujours deux taps. Un vrai bug serait plus erratique.
- L'onglet Network de DevTools : rechargez, ne touchez à rien, et observez. Les scripts du menu (souvent jQuery + le script du thème) ne se téléchargent qu'après votre première interaction. C'est la signature du délai.
- Test d'élimination : désactivez temporairement le JS delay. Si le burger répond au premier tap, le coupable est identifié.
Solution A : isoler le menu ET ses dépendances
Le réflexe naturel est d'exclure le script du menu du délai. C'est nécessaire, mais souvent insuffisant, et c'est là que la plupart des gens restent bloqués.
Un menu de thème ne fonctionne presque jamais seul : il dépend d'autres scripts, typiquement jQuery (et parfois jQuery Migrate, ou le cœur du thème). Si vous excluez le script du menu mais laissez jQuery dans le délai, le script du menu s'exécute alors que jQuery n'existe pas encore : il échoue silencieusement ou s'attache à rien. Résultat : le menu reste cassé, et vous ne comprenez pas pourquoi, puisque « le menu est exclu ».
Il faut donc exclure toute la chaîne : jQuery → (jQuery Migrate) → le script du menu. Ensemble.
Deux pièges concrets lors de la déclaration de ces exclusions :
- Ciblez via un fragment unique de l'URL du fichier (par exemple
jquery.min.jsou le nom du script du thème), pas via son « handle » interne : selon l'outil, le suffixe technique peut être retiré et l'exclusion rate sa cible. - Gardez un garde-fou sur l'état du document : le script exclu doit tolérer une exécution précoce (vérifier que le DOM est prêt avant de s'attacher au bouton).
Le compromis à accepter : en sortant jQuery et le menu du délai, vous rechargez une partie du JavaScript tôt, donc vous perdez une partie du gain de performance. C'est acceptable si vous voulez garder le menu du thème tel quel, mais ce n'est pas gratuit.
Solution B (le meilleur cas) : un menu vanilla, sans dépendance
La vraie solution durable, en particulier sur un thème lourd (Divi, par exemple, où c'est le menu qui impose jQuery), consiste à ne plus du tout faire dépendre le menu de jQuery.
Un menu burger, dans le fond, est trivial : un bouton qui bascule une classe sur la navigation. Quelques lignes de JavaScript natif suffisent, sans aucune dépendance :
<button class="nav-toggle" aria-expanded="false" aria-controls="site-nav">Menu</button>
<nav id="site-nav" class="site-nav"><!-- links --></nav>
<script>
const btn = document.querySelector('.nav-toggle');
const nav = document.getElementById('site-nav');
btn.addEventListener('click', function () {
const open = nav.classList.toggle('is-open');
btn.setAttribute('aria-expanded', open);
});
</script>Comme ce script est minuscule et sans dépendance, deux options s'ouvrent :
- vous l'inlinez tel quel, non différé, dans le thème : le burger répond au premier tap, toujours ;
- et vous pouvez alors différer absolument tout le reste de votre JavaScript en toute sérénité, puisque la seule interaction critique (l'ouverture du menu) ne dépend plus de rien.
Bonus non négligeable : sur de nombreux thèmes, le menu est la seule raison pour laquelle jQuery se charge en priorité. Un menu vanilla peut donc vous permettre de sortir jQuery du chemin critique entièrement. C'est le meilleur des deux mondes : un menu instantané et un JavaScript allégé.
Avec Mantys Core
Mantys Core gère précisément les deux voies. Pour la solution A, ses exceptions de délai se déclarent par un fragment d'URL (pas le handle fragile) et prennent en compte la chaîne de dépendances, avec le garde-fou sur l'état du document qui fait que l'exclusion tient vraiment au lieu d'échouer silencieusement.
Et pour la solution B, l'approche « bloc autonome, non différé » est native : un menu vanilla peut être servi hors du délai pendant que tout le reste du JavaScript est reporté, sans que vous ayez à jongler entre score et fonctionnalité.
Autrement dit : que vous gardiez le menu du thème ou passiez au vanilla, le burger répond au premier tap, sans sacrifier l'allégement du reste.
En résumé
- Le « deuxième clic obligatoire » vient du JS delay : le premier tap réveille le chargement du script au lieu d'ouvrir le menu.
- Confirmez-le en observant que les scripts ne se chargent qu'après la première interaction (ou en désactivant le délai).
- Solution A : excluez du délai le menu ET ses dépendances (jQuery inclus), en ciblant par URL, avec un garde-fou d'état. Ça fonctionne, mais ça coûte une partie du gain.
- Solution B (le meilleur cas) : un menu vanilla sans dépendance, inliné non différé, qui répond au premier tap et vous laisse différer tout le reste, jQuery inclus.