Avant de mettre en place cette solution, voir en bas de page les contraintes et choses restant à améliorer !!
Simulation : Menu déroulant Flexbox.
Pré-requis :
- Le premier niveau doit occuper 100% de l’espace disponible en largeur,
- les libellés de premier niveau ne doivent pas occuper un espace à largeur fixe (chaque libellé à une largeur en fonction de sa longueur),
- les espaces entre les libellés de premier niveau doivent être strictement identiques,
- les sous-niveau doivent être centrés horizontalement sur l’axe médian libellé de premier niveau > sous-niveau,
- compatibilité navigateurs avec prise en charge ciblée des différents états de la spécification de Flexbox (ancienne et nouvelle) et fallback IE10-.
Quelques aspects techniques :
- [CSS|Flexbox] Les écarts gauche/droite entre chaque entrées sont distribués de façon homogène, grâce à flexbox.
[jQuery] Chrome qui semble ne pas interpréter correctement la présence de paddings dans le sous menu lorsqu’on utilise la propriété outerWidth de jQuery pour calculer le positionnement centré horizontalement du sous menu par rapport à son entrée parente.
Edit: ajout d’un span.medial-axis-alignment pour englober les libellés de 1er niveau sans les marges et calculer en jQuery le positionnement des sous-niveaux par rapport à l’axe médiant.[CSS] position: relative; qui n’est pas bien gérée sous Firefox avec display: table; et display: table-cell;.Edit: le menu est passé d’une structure fondée sur le modèle tabulaire à une structure fondée sur flexbox. Le modèle tabulaire reste proposé en guise de fallback pour les navigateurs IE9-.
Remarques importantes pour la compatibilité cross-browsers :
- Pour effectuer le switch entre vieille et nouvelle spécification Flexbox, les styles ont été déclarés dans l’ordre suivant :
- 1. les propriétés propres à l’ancienne spécification avec les préfixes -webkit, -moz, -o, et sans préfixe :
123456789101112131415/* Old 2009 version support *//* Firefox 27-, Chrome 20-, Safari 6- */.primary-nav .level1{display: -webkit-box;display: -moz-box;display: -o-box;display: box;}.primary-nav .level1 li{-webkit-box-flex: 1;-moz-box-flex: 1;-o-box-flex: 1;box-flex: 1;} - 2. les propriétés propres à la nouvelle spécification avec un suffixe !important.
12345678/* Current specification */.primary-nav .level1{display: flex !important;}.primary-nav .level1 li{flex-grow: 1 !important;}
- 1. les propriétés propres à l’ancienne spécification avec les préfixes -webkit, -moz, -o, et sans préfixe :
- Ciblage CSS IE9, IE8 obtenu avec la mise en place de commentaires conditionnels dans la balise head de la page et de classes .ie8, .ie9 dans la CSS :
1234567<!--[if IE 8 ]><html lang="fr" class="ie8"><![endif]--><!--[if IE 9 ]><html lang="fr" class="ie9"><![endif]-->
123456789.ie8 .primary-nav .level1,.ie9 .primary-nav .level1{display: table;}.ie8 .primary-nav .level1 li,.ie9 .primary-nav .level1 li{display: table-cell;} - Ciblage CSS IE10 obtenu avec la mise en place de la méthode décrite ici : IE 10 Specific Styles.
123/* Deux lignes de javascript pour ajouter le User Agent à l'élément <HTML> */var doc = document.documentElement;doc.setAttribute('data-useragent', navigator.userAgent);
1234/* Styler à partir du User Agent */html[data-useragent*='MSIE 10.0'] h1 {color: blue;}
Code jQuery à exécuter pour centrer les sous-menu par rapport aux largeurs des premiers niveaux
1 2 3 4 5 6 7 8 |
jQuery('.primary-nav .level2').each(function() { var nav_sub_item_width = 220; /* if you modify this value, remember to modify it in 'navigation.css' too */ var nav_primary_item_width = jQuery(this).closest('.medial-axis-alignment').outerWidth(); var wanted_value = (nav_sub_item_width / 2) - (nav_primary_item_width / 2) + 12; //var wanted_value = (nav_sub_item_width / 2) - (nav_primary_item_width / 2); /* remove padding value in some cases */ jQuery(this).css('margin-left', -wanted_value); }); |
Cas particuliers et adaptation de code à apporter :
Dans le cas où les sous-menus ne sont pas correctement centrés par rapport à l’axe médian, lancer le bout de code jQuery seulement après que la page se soit entièrement chargée :
1 2 3 |
$(window).bind("load", function() { // code here }); |
…ou mieux (sans le bind) :
1 2 3 |
jQuery(window).load(function() { // code here }); |
Remarque à propos de la gestion des paddings par jQuery (notamment sous Chrome) : pour une raison obscure, la valeur du padding (+ 12 dans le calcul de la valeur servant à aligner le sous-menu sur l’axe médian du libellé de premier niveau) concourt à obtenir un alignement correct dans la simulation effectuée par le biais de JSFiddle, mais il a fallu la supprimer dans un autre cas de figure rencontré…
1 |
var wanted_value = (nav_sub_item_width / 2) - (nav_primary_item_width / 2) + 12; |
Décalage du sous-menu sous Chrome – appliquer la solution ci dessous :
1 2 3 |
.primary-nav .level1 li:hover .level2{ left: -12px; /* IN SOME CASES - fixes Chrome medial axis vertical alignment bug */ } |
Polyfill IE9- (non-testé – nécessite Selectivizr) : Flexie.js.
A améliorer : l’alignement vertical des libellés de 1er niveau n’est pas géré.