Menu déroulant horizontal

Extrait de code pour un menu déroulant, horizontal et adaptatif avec bouton dit « hamburger ».

Règles d’implémentation pour l’accessibilité

  • L’exemple présenté ici est celui d’un menu hamburger avec menu déroulant (rubriques parentes et sous-rubriques).
  • La page affichée est celle correspondant au lien 3 de la rubrique 2.
  • Le bouton hamburger est une icône : il est donc masqué aux logiciels de synthèse vocale par l’attribut aria aria-hidden="true" et un texte significatif est proposé pour ces logiciels mais caché à la vue via le style css sr-only.
  • Le bouton de rubrique parente permettant d’afficher le sous-menu correspondant :
    • porte l’attribut aria aria-controls qui fera référence à l’ìd posé sur la liste contenant le sous-menu ;
    • porte l’attribut aria aria-haspopup="true" pour signifier aux technologies d’assistance qu’un sous-menu est présent ;
    • porte l’attribut aria aria-expanded qui prend la valeur true si le sous-menu est affiché, false si le sous-menu n’est pas affiché.
  • Pour que ce menu fonctionne, du code javascript sera nécessaire. Le script gère :
    • le changement d’état aria-expanded ; l’affichage/disparition du sous-menu est alors géré en CSS ;
    • la disparition du sous-menu quand l’utilisateur appuie sur la touche “Echap” ou clique n’importe où dans la page. De plus, le focus revient sur le bouton à l’origine de l’action.

Visuel du menu proposé

Le menu proposé est celui du site “Sofève Concept”

  • Menu replié en vue supérieure à 1200px de large
    menu replié - largeur minimum 1200px

  • Menu déplié en vue supérieure à 1200px de large
    menu déplié - largeur minimum 1200px

  • Menu replié en vue responsive (largeur d’écran inférieure à 1200px)
    menu replié - largeur inférieure à 1200px

  • Menu déplié en vue responsive (largeur d’écran inférieure à 1200px)
    menu déplié - largeur inférieure à 1200px

Extraits de code

Code HTML

 <nav role='navigation' class="navbar" aria-label="Menu principal">
                <button aria-haspopup="true" aria-expanded="false" id="hamburger" aria-controls="menu">
                    <span class="sr-only">Menu</span></button>
                <ul id="menu">
                    <li class="home"><a href="xxx">
                        <i class="fa fa-home" aria-hidden="true"></i>
                        <span class="responsive">Accueil</span></a>
                    </li>
                    <li>
                        <button aria-haspopup="true" aria-expanded="false" aria-controls="menu-1">Première rubrique</button>
                        <ul id="menu-1">
                            <li><a href="xxx">Veniam nulla sunt ullamco commodo</a></li>
                            <li><a href="xxx">commodo nostrud consectetur</a></li>
                            <li><a href="xxx">Elit do ex commodo culpa tempor</a></li>
                        </ul>
                    </li>
                    <li><button aria-haspopup="true" aria-expanded="false" aria-controls="menu-2" aria-current="true">Deuxième rubrique</button>
                        <ul id="menu-2">
                            <li><a href="xxx">Veniam nulla sunt ullamco commodo</a></li>
                            <li><a href="xxx">commodo nostrud consectetur</a></li>
                            <li><a href="xxx" aria-current="page">La page active</a></li>
                            <li><a href="xxx">Elit do ex commodo culpa tempor</a></li>
                            <li><a href="xxx">voluptate qui mollit do cillum id</a></li>
                        </ul>
                    </li>
                    <li><button aria-haspopup="true" aria-expanded="false" aria-controls="menu-3">Troisième rubrique</button>
                        <ul id="menu-3">
                            <li><a href="xxx">Veniam nulla sunt ullamco commodo</a></li>
                            <li><a href="xxx">commodo nostrud consectetur</a></li>
                            <li><a href="xxx">voluptate qui mollit do cillum id</a></li>
                            <li><a href="xxx">Exercitation nisi labore laborum</a></li>
                        </ul>
                    </li>
                </ul>
            </nav>

Code CSS

Pour adapter la couleur de la barre de navigation et des liens, modifier les codes couleurs dans la déclaration :root.

:root {
    --FONT-FAMILY-texte:  Arial, Helvetica, sans-serif;
    --FONT-FAMILY-titre:  Arial, Helvetica, sans-serif;
    --CADRATIN-base: 1em;

    --WHITE-color: #FFFFFF;
    --WHITE-TRANSPARENT-color: rgba(255, 255, 255, 0.8);
    --BLACK-color: #000000;
    --BLACK-TRANSPARENT-color: rgba(0, 0, 0, 0.8);

    --BANDEAU-color: #474747;
    --BANDEAU-ACTIVE-color: #87c300;
    --BANDEAU-ACTIVE-CONTRAST-color: #000;
    --BANDEAU-TRANSPARENT-color: rgb(71, 71, 71, 0.8);
    --LINK-color: #4A6B00;
    --OUTLINE-color: 3px solid #588000;
    --BORDER-color: #575757;

    --SHADOW: 0 0.5rem 1rem #757575 !important;
   
}
 /** Navbar - Barre de navigation horizontale principale et menu déroulant */

.sr-only {
    border: 0 !important;
    clip: rect(1px, 1px, 1px, 1px) !important;
    -webkit-clip-path: inset(50%) !important;
    clip-path: inset(50%) !important;
    height: 1px !important;
    overflow: hidden !important;
    padding: 0 !important;
    position: absolute !important;
    width: 1px !important;
    white-space: nowrap !important;
  }

nav[role="navigation"].navbar {
    background-color: var(--BANDEAU-color);
    color: var(--WHITE-color);
    min-height: 2.5em;
}

nav[role="navigation"].navbar .responsive {
    clip: rect(1px, 1px, 1px, 1px);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
}
nav[role="navigation"].navbar  button#hamburger {
    display: none;  
}
nav[role="navigation"].navbar button#hamburger::before {
  font-family: 'FontAwesome';
  content: "\f0c9";
}
nav[role="navigation"].navbar ul {
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
    list-style: none;
}

nav[role="navigation"].navbar ul button[aria-expanded='false']+ul {
    display: none;
}

nav[role="navigation"].navbar ul li {
    text-align: center;
    border-right: 1px solid var(--WHITE-color);
}

nav[role="navigation"].navbar ul li a[aria-current="page"],
nav[role="navigation"].navbar ul li a,
nav[role="navigation"].navbar ul li button {
    padding: 0.75rem 1.5rem .5rem 1.5rem;
    font-family: var(--FONT-FAMILY-texte);
    font-size: 1rem;
    line-height: 1em;
    color: var(--WHITE-color);
}

nav[role="navigation"].navbar  ul li a[aria-current="page"],
nav[role="navigation"].navbar  ul li button[aria-current="true"] {
    background: var(--BANDEAU-ACTIVE-color);
    color: var(--BANDEAU-ACTIVE-CONTRAST-color);
    font-weight: bold;
}

nav[role="navigation"].navbar ul li a {
    display: block;
    text-align: left;
    text-decoration: none;
}

nav[role="navigation"].navbar ul li button {
    background-color: transparent;
    border: 0;
    cursor: pointer;
    width: 100%;
    text-align: left;
}

nav[role="navigation"].navbar ul li button::after {
  font-family: 'FontAwesome';
  content: "\f078";
  float: right;
  padding-left: .5em;
}

nav[role="navigation"].navbar ul li a:hover,
nav[role="navigation"].navbar ul li a:focus,
nav[role="navigation"].navbar ul li button:hover,
nav[role="navigation"].navbar ul li button:focus,
nav[role="navigation"].navbar ul li button[aria-expanded='true'] {
    background: var(--BANDEAU-ACTIVE-color);
    color: var(--BANDEAU-ACTIVE-CONTRAST-color);
    text-decoration: underline;
}

nav[role="navigation"].navbar ul li button[aria-expanded="true"]::after {
    transform: rotate(-180deg);
  }


nav[role="navigation"].navbar ul button[aria-expanded='true']+ul {
    display: block;
    box-shadow: var(--SHADOW);
    background-color: white;
    position: absolute;
    margin-top: 0;
    z-index: 1030;
}
/*si on veut un triangle vers le bas*/
nav[role="navigation"].navbar ul button[aria-expanded='true']+ul::before {
    content: "";
    display: block;
    border: 0.5em solid var(--WHITE-color);
    border-top-color: var(--BANDEAU-ACTIVE-color);
    position: absolute;
    top: 0;
    left: 0.25em;
}


/** style pour les sous-menus */
nav[role="navigation"].navbar ul button[aria-expanded='true']+ul {
    padding-bottom: .5em;
}
nav[role="navigation"].navbar ul button[aria-expanded='true']+ul li:first-of-type {
    padding-top: .5em;
}

nav[role="navigation"].navbar ul button[aria-expanded='true']+ul li a[aria-current="page"],
nav[role="navigation"].navbar ul button[aria-expanded='true']+ul li a {
    color: var(--LINK-color);
    background: transparent;
}

nav[role="navigation"].navbar ul button[aria-expanded='true']+ul li a:hover,
nav[role="navigation"].navbar ul button[aria-expanded='true']+ul li a:focus {
    text-decoration: underline;
    background-color: transparent;
}
nav[role="navigation"].navbar ul button[aria-expanded='true'] + ul li a[aria-current="page"] {
    font-weight: bold;
    
    
}
nav[role="navigation"].navbar ul button[aria-expanded='true'] + ul li a[aria-current="page"]::before{
    content: "";
    float: left;
    height: 1em;
    margin-left: -.5em;
    margin-right: .25em;
    width: 3px;
    background-color: var(--LINK-color);
}
nav[role="navigation"].navbar ul button[aria-expanded='true'] + ul li a[aria-current="page"]:first-of-type a{
    padding-top: .75em;
}

/** Responsive */
@media screen and (max-width: 1200px) {
 
   nav[role="navigation"].navbar button#hamburger {
        display: block;
        background-color: transparent;
        border:0;
        color: var(--WHITE-color);
        font-size: 1.2em;
        font-weight: bold;
        padding: 0.25em 1em 0.5em 1em;;
        cursor: pointer;
    }
   nav[role="navigation"].navbar button#hamburger:hover {
        background: #B0AEAE;
        color: #191A1A;
    }
   nav[role="navigation"].navbar button[aria-expanded=false]#hamburger + ul {
        display: none;
    }
    nav[role="navigation"].navbar ul button[aria-expanded='true']+ul::before {
        content: none;
    }
   nav[role="navigation"].navbar ul {
        flex-direction: column;
    }

   nav[role="navigation"].navbar ul li {
        text-align: left;
        border: 0;
    }

   nav[role="navigation"].navbar ul li:not(:last-child) {
        border-bottom: 1px solid var(--WHITE-color);
    }

    nav[role="navigation"].navbar .icon-home {
        display: none;
       } 
   nav[role="navigation"].navbar .responsive {
        position: static;
        width: auto;
        height: auto;
        overflow: visible;
        clip: auto;
        white-space: normal;
    }

    nav[role="navigation"].navbar ul button[aria-expanded='true']+ul {
        display: block;
        box-shadow: none;
        background-color: var(--WHITE-TRANSPARENT-color);
        position: static;
        margin-top: 0;
        z-index: 1000;
    }
   nav[role="navigation"].navbar button[aria-expanded='true'] + ul li  ul li:not(:last-child){
        border-bottom:1px solid var(--WHITE-color);
      }
   nav[role="navigation"].navbar ul button[aria-expanded='true']+ul li {
        border: 0;
    }

}

Code javascript

Le script ci-dessous sera intégré dans la page HTML via l’écriture suivante :

<script src="/js/script.js"></script>

Fichier js/script.js

/**
 * Fonction pour afficher le sous-menu au clic de souris 
 */
function hamburger() {
  var buttonHamburger = document.getElementById("hamburger");

  // Au clic sur le bouton, on modifie la valeur de l'attribut aria-expanded (true ou false)
  // L'affichage/disparition du sous-menu est ensuite géré en CSS.
  buttonHamburger.addEventListener('click', function (e) {
    let state = buttonHamburger.getAttribute('aria-expanded');
    state === 'false' ? buttonHamburger.setAttribute('aria-expanded', 'true') : buttonHamburger.setAttribute('aria-expanded', 'false');
  });  

  // On gère la disparition du sous-menu quand l'utilisateur appuie sur le bouton "Echap" de son clavier.
  // On s'assure que le focus clavier revient bien sur le bouton "Hamburger".
  document.addEventListener('keydown', evt => {
    var isEchap = false;
    if ("key" in evt) {
      isEchap = (evt.key === "Escape" || evt.key === "Esc");
    } else {
      isEchap = (evt.keyCode === 27);
    }
    if (isEchap) {
      buttonHamburger.setAttribute('aria-expanded', 'false') 
      buttonHamburger.focus()
        }
  });
}

hamburger()

// Le menu et sous-menu

function menuEtSousMenuDeroulant() {

  var theButtons = document.querySelectorAll('.navbar button[aria-expanded]:not(#hamburger)');

  for (i = 0; i < theButtons.length; i++) {

    // apparition/disparition du sous-menu au clic
    // L'affichage/disparition du sous-menu est ensuite géré en CSS.
    theButtons[i].addEventListener('click', function (e) {
      var thisButton = e.target;
      for (j = 0; j < theButtons.length; j++) {
        if (thisButton !== theButtons[j])
          theButtons[j].setAttribute('aria-expanded', 'false')
      }
      var stateButton = thisButton.getAttribute('aria-expanded') === 'false' ? true : false;
      thisButton.setAttribute('aria-expanded', stateButton);
    });

    // disparition des sous-menu quand changement de focus sur bouton
    theButtons[i].addEventListener('focus', function (e) {
      var thisButton = e.target;
      for (j = 0; j < theButtons.length; j++) {
        if (thisButton !== theButtons[j])
          theButtons[j].setAttribute('aria-expanded', 'false')
      }
    });
  }

  // disparition du sous-menu et focus sur le bouton correspondant au sous-menu quand l'utilisateur appuie sur le bouton "Echap" de son clavier.
  document.addEventListener('keydown', evt => {
    var isEchap = false;
    if ("key" in evt) {
      isEchap = (evt.key === "Escape" || evt.key === "Esc");
    } else {
      isEchap = (evt.keyCode === 27);
    }
    if (isEchap) {
      for (j = 0; j < theButtons.length; j++) {
        if (theButtons[j].getAttribute('aria-expanded') === 'true') {
          theButtons[j].setAttribute('aria-expanded', 'false');
          theButtons[j].focus()
        }
      }
    }
  });

}
menuEtSousMenuDeroulant()



// Fermeture de tous les sous-menus via clic dans le body
function fermerMenuViaClicBody(event) {
  var theButtons = document.querySelectorAll('.navbar button[aria-expanded]');
  if (!event.target.matches('.navbar button[aria-expanded]')) {
    for (i = 0; i < theButtons.length; i++) {
      theButtons[i].setAttribute('aria-expanded', 'false');
    }
  }
}

document.body.addEventListener('click', fermerMenuViaClicBody, false);