La Case de l’Oncle Tom

Développement Web, bonnes pratiques et performances

Menu déroulant en rollover semi-accessible avec jQuery

Logo jQuery

Joie : je fais maintenant partie de Planète Accessibilité en plus de Planet Libre. C’est l’occasion pour ce premier article dédié d’allier à la fois logiciels libres et accessibilité pour le plus grand bien du Web ;-)

Le but de cette explication sera de développer la méthode et le raisonnement pour mettre en place un menu en rollover accessible. La difficulté tient essentiellement au fait que tout élément masqué par le biais des CSS est masqué pour de nombreux clients Web.

Nous verrons aussi pourquoi il est important de dissocier la présentation et les artifices graphiques grâce à une dégradation propre du JavaScript. Nous utiliserons pour cela jQuery, ma librairie JavaScript favorite.

Buts et objectifs de l’exercice

J’ai eu besoin d’appliquer cette technique sur le site Emu Nova. Je souhaitais plusieurs choses :

  • disposer d’un menu en rollover en haut de page (facilite la navigation sur toutes les pages)
  • placer le contenu avant les menus dans le flux HTML de la page (améliore le référencement)
  • une navigation possible sans JavaScript (pour cause de bug ou autre)

Autrement dis, j’ai besoin d’un contenu en fin de flux mais visible avant tout le reste. J’ai opté pour la solution JavaScript pour éviter un positionnement absolu pénible à gérer (pour cause de conteneur centré) mais aussi pour respecter le colonnage de Blueprint (puis toutes façons j’ai raison :-P)

En une image, cela se résume ainsi :

Tentative de menu accessible

Nous allons aborder les 3 phases de ce tracé de flèche :

  1. la construction du menu (la zone verte)
  2. le déplacement du contenu (la flèche)
  3. la construction de notre nouveau menu (la zone bleue)

Étape 1 : construire le menu en HTML

C’est l’étape essentielle. De sa structure dépend le reste de l’application. On doit d’abord penser à présenter le contenu de manière dégradée. C’est ainsi que le verront les utilisateur et c’est important de penser d’abord au pire des cas avant de mettre en place les paillettes et les artifices.

Menu accessible (Étape 1)

Cette structure est représentée ainsi en HTML :

<div id="contenu-secondaire">
  <ul id="navigation">
    <li class="first column span-6">
      <h2>Actualités</h2>
      <ul>
        <li><a href="...">Actualités</a></li>
        <li><a href="...">Newsletter</a></li>
        <li><a href="...">Flux RSS</a></li>
        <li><a href="...">Twitter</a></li>
        <li><a href="...">Réactions à chaud</a></li>
      </ul>
    </li>
    <li class="column span-6">
      <h2>Émulation</h2>
      <ul>
        <li><a href="...">Émulation</a></li>
        <li><a href="...">Tutoriaux</a></li>
        <li><a href="...">Foire aux Questions</a></li>
      </ul>
    </li>
  </ul>
</div>

Cette version est volontairement tronquée pour faciliter sa lecture.
L’idée générale de tout ça c’est de transporter directement la liste #navigation en dehors de son conteneur, #extra-content. C’est en effet plus rapide et plus performant de transporter une partie du DOM dans un autre endroit que de la recréer séquentiellement.

Ça aura également l’avantage de limiter au maximum le travail à effectuer en JavaScript par derrière. On notera que jusqu’à présent, on n’a pas encore touché à jQuery.

Avec cette structure, on peut dores et déjà deviner que les <ul> de second niveau seront masqués et affichés à la demande.

Étape 2 : préparer le menu HTML

La deuxième reste assez simple : on transporte #navigation dans son nouveau conteneur (déjà existant) : #welcome-bar.

Pour éviter tout aléas graphique, l’idéal est de masquer tout ce qu’on ne veut pas voir maintenant. Nous rentrons maintenant dans la partie pure JavaScript.

Menu accessible (Étape 2)

Pour atteindre ce résultat, on pourrait envisager le code suivant :

(function($){
  $(function(){
    /*
     * Étape 2 : préparation du menu
     */
    $('#navigation > li > ul').hide();
    $('#navigation').appendTo('#welcome-bar');
  });
})(jQuery);

En soi, ce n’est pas excessif du tout :

  1. on masque tous les sous-menus
  2. on transporte le contenu de #navigation dans #welcome-bar

Tout le travail se situait dans la réflexion il faut croire ;-)

Étape 3 : assigner les évènements

Maintenant le plus dur c’est de donner vie à tout ça. C’est bien beau d’avoir des menus mais encore faut-il les animer. Là encore nous avons plusieurs contraintes à subir :

  • les titres doivent être cliquables. Certaines personnes cliqueront en effet avant de réaliser qu’il y a un menu déroulant. Par principe, on prendre le premier lien de la liste et on l’assignera au titre correspondant ;
  • le menu doit se dérouler lors du survol du titre
  • mais il ne doit pas se masquer tant qu’on n’a pas quitté le titre NI la liste déroulante

La difficulté tient à ces 2 dernières contraintes. On pourrait tout d’abord penser à l’utilisation des évènements mouseover et mouseout MAIS, parce qu’il y a bien un mais, mouseout est un peu capricieux.

Si on imagine un mouseover directement sur li.column, le problème c’est que survoler un élément comme Actualités ou Newsletter activera le mouseover de ces derniers … et désactivera, un temps minime certes, le survol de li.column. Autrement dit le menu se repliera alors qu’on tentera de l’utiliser.

Heureusement pour nous, jQuery a introduit les évènements mouseenter et mouseleave (présents dans Internet Explorer depuis des lustres, c’est bien le seul avantage de cette atrocité) depuis la version 1.2.2. Ces évènements correspondent exactement à ce que l’on souhaite : maintenir une zone survolée malgré le survol de ses enfants.
Tout est histoire de couches ;-)

Menu accessible (Étape 3)

Côté code, ça se complique :

(function($){
  $(function(){
    /*
     * Étape 3 : assignation des évènements et transformation
     */
    $('#navigation > li')
      .each(function(){
        var title = $('h2:first', this);
        var href = $('a:first', this).clone();
        href.text(title.text()).wrapInner('<span></span>');
        title.html(href);
       })
      .find('h2 > a').bind('mouseenter', function(){
        $(this).parents('li.column').find('> ul').slideDown('fast');
      }).end()
      .bind('mouseleave', function(){
        $('> ul', this).slideUp();
      });
  });
})(jQuery);

Plusieurs remarques sur ce code :

  • j’utilise les chaînes de jQuery pour gagner du temps et reparcourir des tableaux déjà connus (le sélecteur n’est pas réexécuté)
  • j’utilise également la méthode end() pour revenir au précédent état du sélecteur. Très pratique pour naviguer dans un jeu d’éléments et gagner en performances

Et pour les explications :

  1. Pour chaque élément de liste
    1. on met de côté le titre de la liste
    2. on clone le premier élément du sous-menu
    3. on modifie le libellé du lien du clone
    4. on remplace le titre par le code HTML du clone
  2. Pour ces liens hypertextes nouvellement créés (plus faciles à styler sans JS au rollover), on leur demande de déplier le sous-menu voisin
  3. Ce sous-menu ne sera replié que lorsqu’on quittera le li.column

Conclusion

La mise en œuvre de ce menu est relativement aisée et surtout, suffisamment souple pour que vous puissiez l’adapter à vos besoins.
Dans tous les cas on remarquera que les clés de la réussite sont :

  • un code HTML propre (facile les sélections JS et le stylage CSS)
  • une évolution d’une base sans artifices vers une interface améliorée en JavaScript
  • un code simple, facilement maintenable et adaptable

Ceci n’est qu’un exemple où le JavaScript peut servir à conserver des interfaces accessibles tout en augmentant leur utilisabilité. Qui a dit que le JavaScript c’était nul ?

Le seul reproche que l’on peut faire à ce menu accessible c’est le manque de navigation au clavier. L’idéal serait de pouvoir naviguer dans le choix des menus entièrement avec les flèches de son clavier.
Rendez-vous dans un autre billet pour ce point ? ;-)

You don't have a sufficient version of Flash Player to display this animation.

Votre lecture de l'article Menu déroulant en rollover semi-accessible avec jQuery est terminée. Il y a encore plein de choses à lire ici ... vous voulez des idées ?

Commentaires

  1. Syeric dit :

    Un excellent article !

    Plusieurs remarques. Personnellement, lorsque j’applique un hover() (et non la famille mouse*() ), je ne rencontre pas de problème de repli intempestif du menu. J’ai par contre une structure différente de la tienne car je n’utilise pas de ul parent mais plutôt une succession de div. Je dirais que les deux se valent :)

    Ensuite, j’utilise plutôt fadeIn et fadeOut au lieu de slideUp et slideDown. En effet j’ai déjà eu quelques bugs de clignotement intempestifs du menu en utilisant ces derniers, selon la manière dont on présente la souris.

    Bon, ce sont des remarques “détail” bien sûr, ton article m’a beaucoup intéressé dans les aspects accessibilité.

    A+

    Syeric

  2. Je suis tombé sur ton article de menu déroulant “accessible” et j’ai été tout de suite très intéressé. En effet, le “menu déroulant accessible” est le Saint Graal de la conception web et cela fait un sacré bout de temps qu’on cherche à le produire.

    Du coup, j’avoue avoir été plutôt déçu au final puisque beaucoup de critères minimaux d’accessibilité (navigation au clavier, sans couleurs, …) ne sont pas pris en compte.
    En gros, si j’ai bien compris, par “accessible” tu veux simplement dire qu’il fonctionne si JS est désactivé ?

    Aurélien Levy s’était penché sur ce cas épineux et avait réalisé une analyse et un résultat assez probant (mais lourd) : http://www.fairytells.net/index.php/2006/06/26/28-menu-deroulant-et-accessibilite-partie-1

    Attention donc à l’alléchante l’étiquette “accessible” ;)

    Raphaël

  3. Es-tu certain que mettre le HTML de ton menu après le contenu soit très accessible ?

    C’est sûr que se retaper le menu à chaque page avec une synthèse vocale risque d’être pénible, mais c’est pour ça qu’on a couramment des liens d’évitement. Avoir le menu en fin de page, ça suppose de connaître déjà le site, les p’tits nouveaux ne vont pas savoir tout de suite comment le site est organiser, ce qui n’est pas terrible pour l’ergonomie…

  4. Oncle Tom dit :

    @Raphaël : oui c’est un peu racoleur j’admets ;-) On dira qu’il est plutôt semi-accessible car pas navigable au clavier. L’idée était de s’intéresser à la partie technique en JS et à la structure HTML.
    Je vais renommer l’article et j’espère pouvoir lui sortir une suite, avec un plugin jQuery à la clé qui évite la complexité de mise en œuvre décrite (en bien) par Aurélien.

    @Nicolas : en fait je mets des liens d’évitement de manière systématique en haut de page ce qui évite comme tu dis d’avoir à parcourir toute la page pour obtenir le menu. Il faut que j’en parle dans l’article en effet sinon ça n’a aucun intérêt d’avoir un menu tout en bas (ce n’est presque plus un menu je dirais)

  5. Docteur Fas dit :

    Je suis aveugle et j’ai constater sur emunova pour qui tu as fait cette astuce des problèmes nombreux. Avec Jaws 7.10 ou 8.0 et Fx3 la sintèse me lis le href. Dans d’autre condition de test je n’ai pas acces au sous-catégorie… Donc a voir.

  6. Oncle Tom dit :

    @Docteur Fas : retour très intéressant. Tu veux dire que la synthèse vocale lit le href au lieu du texte correspondant ?
    Quelles sont les autres conditions de tests qui t’ont privé d’accès aux sous-catégories ?

    Ça me rappelle que je suis à la recherche de navigateurs à synthèse vocale sous Linux. Je suis preneur à ce niveau là ;)

  7. Docteur Fas dit :

    Alors après quelque test, j’ai remarqué que les problèmes étaient aléatoire…

    Par contre maintenant je sais que le problème se trouve au niveau de la CSS si je la désactive je n’ai plus aucun souci.
    Mais aucune idée pour quelle raison.

    Pour une synthèse vocal sous linux:
    -L’extension “firevox”
    http://www.firevox.clcworld.net/
    A noté que pour l’utilisé je conseil fortement de désactivé toutes les autres extension.

    Oralux :
    Une distribution linux… don je ne sais rien a par son nom :P

  8. Vincent Voyer dit :

    Sympa ! J’ai pu découvrir $(’sélecteur’, this); que je rencontre pour la première fois, cela sert juste à sélectionner ’sélecteur’ à partir à l’intérieur de this ?

    Par contre : le menu déroulant passe derrière les annonces adsense… (test à l’instant, sur FF2 avec annonce textuelle)

    Sinon pour ajouter la navigation au clavier ne suffirait-il pas de modifier légèrement les événements pour :


    $('#navigation > li')
    .each(function(){
    var title = $('h2:first', this);
    var href = $('a:first', this).clone();
    href.text(title.text()).wrapInner('');
    title.html(href);
    })
    .find('h2 > a').bind('mouseenter, focus', function(){
    $(this).parents('li.column').find('> ul').slideDown('fast').end().siblings().find('> ul').slideUp();
    }).end()
    .bind('mouseleave', function(){
    $('> ul', this).slideUp();
    });

    Et hop !

  9. Oncle Tom dit :

    @VincentV : ouaip tout à fait. C’est très pratique pour éviter de marquer en dur du sélecteur partout.
    Par contre c’est curieux pour ton problème de rollover. Sous FF2 Linux, FF3 Windows, IE6/7 Windows le rollover passe dessus. Y’a un z-index en plus :-/

    L’ajout de navigation au clavier est vraiment bien ! Faut que je teste car je pensais déjà partir vers de la capture de touches avec des keypress.

    @Docteur Fas : j’étais tombé sur Firevox. J’ai tenté de l’installer avec Flock mais j’ai dû râter un coche avec l’installation du fichier JAR.

  10. Creamama dit :

    Salut,
    vous pouvez aller voir le site de Grenoble université, http://www.grenoble-universites.fr/ qui gère de facon très habile son menu. je vous laisse contempler.

  11. chamo dit :

    @Creamama : je suis pas trop fan du “filet” blanc sur le site qui s’affiche pour bien faire apparaître les menus. Pour moi c’est plutot une solution de facilité et sa gâche un peu le graphisme. Mais les goût et les couleurs…

Répondre

Balises HTML autorisées : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


Oncle Tom sur Flickr

  1. Kid Mental Preparation
  2. Sword Unarm
  3. Bubblegum Sword Parade
  4. Dasha Baskakova et Julien Perugini (Dasha Trio)
  5. Julien Perugini (Dasha Trio)
  6. Keys Keys Bang Bang
  7. Focus Matters
  8. S-peeing-derman
  9. Mario Star
  10. Phare des Baleines
  11. Matthew Gaul (Anima RPG)
  12. Kisidan (Anima RPG)
  13. Zero (Code Geass)
  14. Cosplay Ayumi Hamasaki
  15. Maki à la bougie
  16. Faire-part de naissance Eyrolles
  17. A Light in the Night
  18. The Shipyards Cranes

Thème graphique par LeslyG, intégré par Oncle Tom.
Propulsé par WordPress, Blueprint et jQuery.