La mise en place d'un menu asynchrone (ne s'affichant que sur demande de l'internaute) permet d'éviter potentiellement les méfaits des méga-menus en SEO. Bien que le débat reste ouvert sur la pertinence et l’intérêt réel de cette méthode en matière de référencement naturel (tout comme sur le fait que ce type d'action soit autorisé ou pas par Google), la réalité démontre que pléthore de sites majeurs, notamment dans le secteur de l’e-commerce, mettent en place des systèmes destinés à masquer a minima leurs sous-menus de navigation. Voyons donc comment procéder…
La création de menus s'affichant de façon asynchrone est une technique de plus en plus utilisée et demandée par les entreprises pour favoriser la transmission du jus de liens. Le fait de créer un tel menu (dont les liens ne s'afficheront que lorsque l'internaute l'aura demandé, en cliquant sur un menu de premier niveau ou tout simplement en passant sa souris par dessus) peut-il être considéré comme de l'obfuscation (ou « offuscation » si l’anglicisme vous dérange) ? Le débat reste ouvert. Est-ce permis par Google ? Là aussi, la question n'est pas clairement tranchée aujourd'hui, et nous lirons les lignes qui suivent avec un œil vigilant et suffisamment de recul sur l'éventuelle pérennité de ce type de pratique...
Nous présentons donc dans cet article une méthode (parmi d’autres…) pour mener à bien cette lourde tâche, que ce soit dans un méga-menu créé à la main en HTML, mais aussi pour un méga-menu ou des sous-menus créés directement avec le CMS WordPress. Ainsi, nous allons pouvoir profiter d’un cas concret de création de menu asynchrone…
Pourquoi créer un méga-menu asynchrone ?
Les moteurs de recherche, Google en tête, valorisent toujours les sites qui obtiennent le plus de liens entrants (backlinks), mais le « jus » obtenu est ensuite réparti au sein des diverses pages (liens sortants) afin de leur diffuser le PageRank acquis. Toutefois, la multiplication des pages dans certains types de sites, et donc des liens sortants des pages, entraîne de facto une répartition trop importante, et donc une valorisation bien trop faible pour les pages qui comptent. En effet, que du PageRank se répartisse plus ou moins équitablement au sein d’un site d’une dizaine de pages reste cohérent, même si certaines de ces pages n’a que peu d’intérêt d’un point de vue du référencement (par exemple une page de références clients ou de mentions légales).
En revanche, quand un site contient des centaines, voire des milliers de pages ou plus, la répartition du jus de lien en interne prend alors tout son sens. Un site e-commerce peut espérer valoriser davantage ses pages de premier niveau (catégories principales de produits par exemple) en leur attribuant plus de jus de liens qu’à d’autres pages. C’est là que l’« asynchronisation » du menu de navigation intervient, en tentant de n'afficher les liens de niveaux inférieurs que sur action de l'internaute, et donc d’empêcher une transmission de jus non désirée (ou tout du moins pas aussi importante qu’à l’accoutumée), ce qui est le principal défaut des méga-menus.
Cette action peut agir directement dans les sous-niveaux d’un menu, donc soit dans un sous-menu classique, soit dans un méga-menu (contenant en général beaucoup plus de liens). Par défaut et si vous ne faites rien de spécial, les moteurs de recherche comme Google vont voir l’ensemble des liens de divers niveaux et répartir le plus équitablement possible de jus de liens à l’ensemble de ces relations hypertextes. Imaginons qu’un méga-menu puisse contenir ne serait-ce qu’une cinquantaine de liens, le PageRank serait alors divisé autant de fois pour ne donner que 1/50e de sa valeur à chaque lien, soit une bien trop faible valeur (et dans cet exemple, nous ne comptons pas tous les autres liens de la page et des autres méga-menus potentiels…).
L’astuce ici consiste donc à limiter le nombre de liens qui vont pouvoir profiter de la répartition du jus de liens, tout en restant fonctionnel pour l’utilisateur, et sans risque de pénalité de la part des moteurs (dans la mesure du possible ^^).
Rappel succinct sur la lecture des robots
Pour s’assurer que notre pratique soit pleinement fonctionnelle, il convient de bien prendre en compte les progrès des moteurs de recherche en matière d’indexation et de lecture des pages. En général, masquer des liens ou un sous-menu complet passe par du code Javascript, lisible en grande partie par les robots les plus évolués. Il en va de même pour l’Ajax (qui dérive de Javascript), souvent utilisé pour afficher des blocs à la volée, qui est généralement lu et suivi par les robots.
Partant de ce constat, la mise en place d'un menu asynchrone est possible mais elle doit être bien réfléchie. Quand il s’agit de liens uniques, on se focalise notamment sur des méthodes de cryptage des URL voire de blocage des robots pour s’assurer que les liens masqués ne puissent pas être retrouvés et suivis. Dans le cas d’un sous-menu ou d’un méga-menu, l’idée est plutôt d’empêcher un potentiel déclenchement de l’événement Javascript par le robot (clic ou survol en général) mais surtout de cacher totalement les sous-menus à afficher dynamiquement. Gardons ces aspects en tête, car c’est ce qui peut nous faire changer de méthode à tout moment (notamment si les crawlers évoluent encore à terme).
Cas n°1 : mise en place d’un méga-menu simple
Avant de passer sur un cas concret avec un méga-menu dans WordPress, nous allons mettre en œuvre le principe désiré en pur HTML et Javascript (jQuery ici). Cet exemple préliminaire va nous permettre de poser les bases du mode de fonctionnement, avec un simple méga-menu qui s’affichera au survol de l’item principal. Voyons comment procéder…
Création du menu en HTML
Tout d’abord, créons une page HTML en ajoutant les éléments habituels (Doctype, Head, Body…), puis plaçons dans le body le code suivant :
<header id="header">
<nav id="navigation">
<ul id="menu">
<li class="cat-1"><a href="#">Catégorie 1</a></li>
<li class="cat-2"><a href="#">Catégorie 2</a></li>
<li class="cat-3"><a href="#">Catégorie 3</a></li>
<li class="cat-4"><a href="#">Catégorie 4</a></li>
<li class="cat-5"><a href="#">Catégorie 5</a></li>
<li class="cat-6"><a href="#">Catégorie 6</a></li>
</ul>
</nav>
</header>
</div>
Nous créons ici un menu à un seul niveau, avec autant d’items principaux que désirés. Il ne faut absolument pas afficher de sous-menus ou méga-menus dans cette étape, c’est le Javascript qui aura cette tâche à réaliser.
Un peu d’habillage en CSS
Pour que le menu soit un peu plus agréable à l’œil, créons un fichier CSS (que nous appellerons style.css pour être très original), et ajoutons une ligne dans le <head> du code HTML pour appeler la feuille de style :
Ensuite, décorons un peu notre menu pour lui donner un semblant de design :
body{background:linear-gradient(45deg, #102eff, #d2379b); font-family:'Roboto', sans-serif; overflow:hidden; width:100%; height:100vh;}
#wrapper {width:80%; margin:0 auto; padding:5em 0;}/* Style du Menu */
#navigation {background:rgba(255, 255, 255, 0.5); border-radius:10px; box-shadow:0 0 25px rgba(0, 0, 0, 0.4), inset 0 -1px 1px rgba(255, 255, 255, 0.8), inset 0 1px 1px rgba(255, 255, 255, 0.8); /*mask-image:linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, #ffffff 20%, #ffffff 80%, rgba(255, 255, 255, 0) 100%);*/}#menu {display:flex; justify-content:center; position:relative;}
#menu li {list-style:none;}
#menu > li > a {display:block; padding:1em 1.6em; color:rgba(16,46,255,0.7); text-decoration:none; border:2px solid transparent;}
#menu > li > a:hover {border-bottom:2px solid rgba(16,46,255,0.7); border-top:2px solid rgba(16,46,255,0.7);}
#menu .megamenu {display:none; position:absolute; top:101%; left:0; width:100%; padding:1em; border-radius: 10px; background:rgba(255, 255, 255, 0.8); z-index:9999;}
#menu > li:hover .megamenu {display:flex;}
#menu .megamenu .col-submenu {flex-basis:67%;}
#menu .megamenu .col-image {flex-basis:33%;}
#menu .megamenu .col-image img {display:block; width:100%;}
#menu .megamenu .submenu > li {margin-right:-4px; display:inline-block; width:50%; margin-bottom:1em; vertical-align:top;}
#menu .megamenu .submenu > li > a {color:#242424; font-weight:bold; margin-bottom:.5em; text-decoration:none; display:block;}
#menu .megamenu .submenu .subsubmenu a {color:#242424; font-size:.95em; line-height:1.4; text-decoration:none; display:block;}
Il ne s’agit que d’un exemple, totalement personnalisable, mais la figure suivante montre le rendu de notre barre de menu de premier niveau, accessible ici par les robots des moteurs de recherche.
Barre de menu de premier niveau
L’asynchronisme comme solution miracle
Passons désormais à l’affichage des sous-menus (méga-menu). Comme nous l’avons prévu, seuls les items de premier niveau sont accessibles par défaut, il convient juste de charger à la volée les sous-éléments lorsqu’un utilisateur clique ou survole un des items présents.
La première étape consiste à créer un petit code Javascript pour faire un appel Ajax vers un fichier contenant les sous-menus. Il est plus simple de passer par jQuery pour faire des requêtes en Ajax, c’est pourquoi nous allons charger la bibliothèque jQuery (via les librairies proposées par Google ici) et créer un fichier jquery.megamenu.js pour accueillir notre programme. Il convient bien évidemment de relier ces fichiers au site en plaçant les deux lignes suivantes dans le code :
Dans notre fichier Javascript, voici le code à créer :
$("#menu > li").on('mouseover', function() {
var _self = $(this); // Élément courant// Vérifie si le sous-menu a déjà été chargé ou non
if(_self.find(".megamenu").length == 0) {
// Requête Ajax pour afficher le sous-menu correspondant
$.ajax({
url: "sous-menu.php",
type: "post",
success: function(data) {
_self.append(data);
}
});
}
});
})(jQuery);
Ce programme est très simple, il vérifie si un survol est effectué sur un item principal du menu. Ensuite, il vérifie si le sous-menu a déjà été chargé ou non (pour éviter de multiplier les appels en Ajax), et si ce n’est pas le cas, il appelle un fichier sous-menu.php pour charger le sous-menu à afficher.
Cela consiste donc juste à charger les éléments qui composent le méga-menu en fonction des actions des utilisateurs, sans que les robots ne puissent déclencher ces événements (survol dans notre cas). Il ne nous reste qu’à remplir le fichier sous-menu.php par un exemple de méga-menu (avec deux colonnes de menus et une image à droite pour s’approcher de ce que l’on rencontre parfois).
<div class="col-submenu">
<ul class="submenu">
<li><a href="#destination-non-offusquee">Sous-catégorie 1</a>
<ul class="subsubmenu">
<li><a href="#">item 1</a></li>
<li><a href="#">item 2</a></li>
<li><a href="#">item 3</a></li>
</ul>
</li>
<li><a href="#">Sous-catégorie 2</a>
<ul class="subsubmenu">
<li><a href="#">item 1</a></li>
<li><a href="#">item 2</a></li>
<li><a href="#">item 3</a></li>
</ul>
</li>
<li><a href="#">Sous-catégorie 3</a>
<ul class="subsubmenu">
<li><a href="#">item 1</a></li>
<li><a href="#">item 2</a></li>
<li><a href="#">item 3</a></li>
</ul>
</li>
<li><a href="#">Sous-catégorie 4</a>
<ul class="subsubmenu">
<li><a href="#">item 1</a></li>
<li><a href="#">item 2</a></li>
<li><a href="#">item 3</a></li>
</ul>
</li>
</ul>
</div>
<div class="col-image">
<img src="image.jpg" alt="Image"/>
</div>
</div>
Notre chargement asynchrone est donc réalisé. Voici à quoi ressemble le résultat final lorsqu’un usager survole un item de premier niveau :
Affichage du mega menu affiché lors d’un survol sur un item principal
Désindexation et blocage des fichiers sensibles
Une ultime étape peut permettre d’aller plus loin dans la possibilité de bloquer les robots des moteurs de recherche. En effet, jusqu’à présent, notre exemple est fonctionnel et n’affiche que le code HTML du menu principal par défaut, avant que l’Ajax ne charge dynamiquement le méga-menu lors d’un survol. Or, ce comportement se rapproche un peu de ce que l’on connait avec des défilements infinis ou au clic (sur un bouton « Afficher plus de résultats » par exemple), et nous savons que les crawlers arrivent parfois à continuer la navigation dans ce genre de cas.
Prudence est mère de sûreté, alors tentons d’aller un peu plus loin pour s’assurer que les robots ne puissent pas accéder au méga-menu. Pour ce faire, nous pouvons tenter de bloquer l’accès aux fichiers sous-menu.php et jquery.megamenu.js. Ainsi, les robots ne pourront pas voir ni suivre les liens qui les contiennent.
La méthode la plus simple est de passer par un fichier robots.txt, dans lequel on ajoute quelques directives comme ceci :
# La ligne ci-dessous est conseillée, mais Google n’aime pas trop que des fichiers .js soit désindexé…
disallow: /jquery.megamenu.js
Il est bien entendu possible de remplacer cette solution avec l’entête X-Robots-Tag par exemple ou en allant encore plus loin dans les techniques bloquantes. Parfois, les liens (présents dans le méga-menu) sont également encodés en base64 pour endurcir la potentielle lecture par les robots (en admettant qu’ils aient réussi à atteindre cette étape…). Pour conclure, qui peut le plus peut le moins, et nous ne sommes jamais trop prudents quand il s’agit de protéger des éléments de code que l'on ne veut pas voir indexés ou interprétés par les moteurs.
Dans notre menu statique d’exemple en HTML, le même méga-menu est chargé pour chaque item principal survolé. Cela ne serait bien entendu pas le cas avec un menu généré dynamiquement, il ne faut prendre cet exemple que pour ce qu’il est, à savoir un cas pédagogique uniquement.
Ajoutons également qu’il ne s’agit que d’un exemple de méthode, et qu’il existe bien sûr plusieurs autres manières d’aborder l'affichage asynchrone d’un méga-menu. Il aurait été difficile de dévoiler ici l’ensemble de ces diverses techniques sans vous perdre dans les pages de lecture. 🙂
Cas n°2 : création d’un méga-menu asynchrone avec WordPress
Le premier exemple permettait de mettre en lumière la méthode générale d’une « asynchronisation » de méga-menu. Pour être plus concret, nous allons réaliser le même menu pour un site WordPress, en respectant le fonctionnement initial de WordPress (donc sans passer par une extension de méga-menu, qui imposerait un programme spécifique pour chaque cas unique). Toutes les étapes sont très proches de ce que l’on a vu précédemment, si ce n’est que nous devons l’adapter au contexte de WordPress. Détaillons chaque phase…
Nous considérons qu’un thème WordPress est déjà en place, avec un emplacement de menu existant pouvant accueillir le méga-menu (ici nous utiliserons « primary » mais cela peut être différent selon le thème). Il faudra toutefois modifier un peu le comportement du thème par la suite (ou du thème enfant) pour arriver au même résultat…
Création du menu
La première étape consiste à générer un menu dans WordPress via Apparence > Menus. Nous reprenons dans l’exemple la même structuration que pour l’exemple en HTML, afin d’aboutir au même résultat via le CMS, de manière dynamique.
La principale différence avec la version HTML présentée précédemment réside dans le fait que nous créons le menu complet, à savoir les items de premier niveau mais aussi leurs descendances (qui nous serviront pour afficher nos sous-menus potentiels).
Création d’un menu sous WordPress
Un peu de CSS pour embellir l’ensemble
Le CSS du menu est en tout point identique à celui de l’exemple précédent, reprenons donc son contenu plus haut dans cet article. Le résultat ressemble donc au menu de la figure 1.
Chargement des items de premier niveau
WordPress utilise la fonction PHP wp_nav_menu() pour afficher les menus. Fort heureusement, cette fonction peut accueillir un tableau de paramètres qui va nous permettre de ne conserver que les items de premier niveau.
Ainsi, là où le menu doit être placé, il convient d’ajouter le paramètre 'depth' => 1 dans le tableau pour arriver à nos fins. L’exemple de code suivant affiche notre menu au bon emplacement, avec les bons éléments HTML et CSS (balises, classes, ID…), en ne conservant que les items de premier niveau.
wp_nav_menu(array(
'theme_location' => 'primary',
'menu_id' => 'menu',
'container' => 'nav',
'container_id' => 'navigation',
'container_class' => 'navigation',
'depth' => 1, // N'affiche que le 1er niveau du menu
));
?>
Mise en place du script Ajax
La mise en place du système asynchrone peut commencer, mais WordPress a la particularité de ne pas exécuter l’Ajax aussi simplement qu’habituellement. Il convient d’appeler un script spécifique pour autoriser les appels en Ajax, d’utiliser des hooks précis pour récupérer les données, etc. C’est un peu complexe au premier abord, et cela nous impose de modifier quelque peu le script jQuery présenté dans l’exemple précédent, comme ceci :
$("#menu > li").on('mouseover', function() {
var _self = $(this); // Élément courant
let parent_id = _self.attr('id').substring(_self.attr('id').lastIndexOf('-') + 1);
let classes = _self.attr('class'); // Vérifie si le sous-menu a déjà été chargé ou non
if(_self.find(".megamenu").length == 0) {
// Requête Ajax pour afficher le sous-menu correspondant
$.ajax({
url: ajaxurl, // URL du script Ajax pour WordPress
method: 'POST',
data: {
'action': 'megamenu_ajax', // Action pour les hooks "Ajax"
'mmid': parent_id,
'mmclass': classes
},
success: function(data) {
_self.append(data);
},
error: function(result, status, error) {
console.log('Erreur (' + error + ') : ' + result);
}
});
}
});
})(jQuery);
Dans cette version du script, les principales différences reposent sur les spécificités de l’Ajax pour WordPress, mais aussi sur notre volonté de remplir le méga-menu dynamiquement avec les bons sous-items de menu. Voici quelques différences :
- Nous récupérons l’identifiant (ID) de l’item du menu parent (variable parent_id) ainsi que les classes CSS de ce dernier (variable classes) pour pouvoir à terme cibler le bon élément (lors de l’affichage des sous-menus).
- Nous devons ajouter un paramètre action avec le nom de notre choix afin de pouvoir exécuter une requête Ajax via WordPress (le nom de cette action sera utilisé dans les hooks appelant les résultats de la requête Ajax). Nous l’appelons megamenu_ajax mais ce nom pourrait être modifié (en pensant à modifier le nom des hooks aussi !).
Bien évidemment, nous devons penser à charger nos fichiers de scripts, dont jQuery. La méthode la plus propose consiste à ajouter ces quelques lignes dans le fichier functions.php du thème WordPress (ou du thème enfant).
wp_enqueue_script('megamenu-offusque', get_template_directory_uri().'/jquery.megamenu.js', array('jquery'), '1.0.0', true);
wp_localize_script('megamenu-offusque', 'ajaxurl', admin_url('admin-ajax.php')); // Pour l'appel Ajax via WordPress
}
Récupération dynamique des éléments du menu
Toute la complication de notre exemple pratique arrive maintenant. Jusqu’à présent, nous avons reproduit globalement l’exemple statique présenté en début d’article, mais nous devons maintenant récupérer dynamiquement les bons éléments des sous-menus de WordPress, pour qu’à chaque survol sur un item principal, le bon méga-menu s’affiche…
Il s’agit d’un exercice périlleux car rien n’est prévu initialement dans WordPress pour faire ce type de manipulation. Nous allons donc devoir prévoir deux fonctions pour d’une part récupérer et hiérarchiser les items de menu (afin d’avoir les descendances à partir du second niveau des items, le premier niveau étant déjà affiché), et d’autre part pour générer le code HTML de chaque méga-menu dynamique.
La première fonction s’appelle get_nav_menu_item_childrens() et reçoit deux paramètres : l’ensemble des items du menu (appelé via la fonction wp_get_nav_menu_items()), et l’ID du menu parent pour ne cibler que les bons éléments enfants. Voici son code récursif :
// Par défaut...
$nav_menu_items_array = array();// Boucle les éléments du menu
foreach($nav_menu_items as $key => &$nav_menu_item) {
// Parcoure uniquement les items qui ne sont pas du premier niveau
if($nav_menu_item->menu_item_parent != 0) {
// S'il s'agit d'un niveau "parent"
if($nav_menu_item->menu_item_parent == $menu_id) {
$children = get_nav_menu_item_childrens($nav_menu_items, $nav_menu_item->ID);
if($children) {
$nav_menu_item->childrens = $children;
}
$nav_menu_items_array[$nav_menu_item->menu_order] = $nav_menu_item; // Ajoute l'élément au tableau
unset($nav_menu_items[$key]); // Retire l'élément du tableau
}
}
}
return $nav_menu_items_array;
}
La seconde fonction, display_nav_menu_item_childrens(), reprend le même principe. Elle génère le bon code HTML des sous-menus à partir de la liste des items hiérarchisés à l’aide de la fonction précédente (le seul paramètre de la fonction ici).
// Par défaut...
$menu_list = '';// On boucle l'ensemble des items
foreach($hierarchical_menu_items as $key => $nav_item) {
// Ajoute un <li>
$menu_list.= '<li>';
$menu_list.= '<a href="'.$nav_item->url.'">'.$nav_item->title.'</a>';
// S'il y a des sous-niveaux <ul>
if(isset($nav_item->childrens)) {
$menu_list.= '<ul class="subsubmenu">';
$menu_list.= display_nav_menu_item_childrens($nav_item->childrens);
$menu_list.= '</ul>';
}
// Ferme le <li> en cours
$menu_list.= '</li>';
}
return $menu_list;
}
Maintenant que nous avons nos deux fonctions, nous pouvons récupérer notre appel Ajax effectué précédemment, puis formatter les méga-menus comme il se doit. Pour ce faire, nous devons utiliser deux hooks de WordPress qui reprennent le nom de l’action ajoutée dans le script Ajax (sous la forme wp_ajax_NOM_ACTION et wp_ajax_nopriv_NOM_ACTION).
add_action('wp_ajax_nopriv_megamenu_ajax', 'megamenu_ajax');
Il ne nous reste qu’à créer la fonction de rappel megamenu_ajax() pour récupérer les bons sous-menus, en fonction des items survolés dans le premier niveau du menu.
$classes = explode(' ', $_POST['mmclass']);if(in_array('menu-item-has-children', $classes)) {
if(($locations = get_nav_menu_locations()) && isset($locations['primary'])) {
$menu = wp_get_nav_menu_object($locations['primary']);
$menu_items = wp_get_nav_menu_items($menu->term_id);
$menu_array = get_nav_menu_item_childrens($menu_items, $menu_parent_id);
$menu_list = '<div class="megamenu">';
$menu_list.= '<div class="col-submenu">';
$menu_list.= '<ul class="submenu">';
$menu_list.= display_nav_menu_item_childrens($menu_array);
$menu_list.= '</ul>';
$menu_list.= '</div>';
$menu_list.= '<div class="col-image"><img src="'.get_template_directory_uri().'/image.jpg" alt="Image"/>';
$menu_list.= '</div>';
echo $menu_list;
}
}
die(); // Important pour WordPress
}
Cet exemple reproduit celui présenté dans le cas n°1 en termes de remplissage et d’esthétique (voir figure 2). Dans le bas de notre dernière fonction, on peut apercevoir l’ajout d’une image identique pour chaque méga-menu, mais nous pourrions très bien adapter le code pour afficher d’autres éléments, des images variables issues des catégories parentes par exemple, etc. Ce code n’était pas important pour l'affichage asynchrone, mais nous avons tout le loisir de personnaliser totalement l’affichage des méga-menus.
L’affichage asynchrone s’effectue jusqu’au premier survol des items principaux, puis les méga-menus s’affichent tout simplement avec du CSS, comme habituellement. Les robots ne peuvent voir que le premier niveau, et nous faisons en sorte d’afficher tout le reste des sous-menus via l’Ajax. Retenons toutefois qu’il est préconisé de désindexer le fichier Javascript de l’Appel Ajax, voire les fonctions PHP de réception (on peut très bien créer un fichier PHP spécifique à bloquer, que l’on appelle via le fichier functions.php du thème WordPress).
À vos claviers ! 🙂
Mathieu Chartier, Consultant-Formateur et webmaster indépendant chez Internet-Formation (https://www.internet-formation.fr)