Un focus trap moderne

Nantes, le 18 mars 2024.

Le besoin

L’exemple classique pour décrire la mise en place d’un piège pour le focus, c’est l’ouverture d’une boîte de dialogue modale.

« Modale » signifie que l’accès à son contenu est exclusif ; le reste (en dehors du dialog) est interdit et cela est signifié de plusieurs manières :

  • graphiquement, avec un arrière-plan (souvent semi-transparent) qui vient recouvrir les parties désactivées ;
  • mécaniquement, avec ce même arrière-plan qui empêche de cliquer sur les éléments qui se situent en dessous ;
  • sémantiquement, avec une désactivation de toute la partie de l’arbre DOM associée (très souvent via un aria-hidden).

Et je n’oublie pas la navigation au clavier (puisque c’est notre sujet) ; celle-ci doit être réduite exclusivement au contenu de la boîte de dialogue :

  1. On détecte le dernier élément focusable (ainsi que le premier) de notre boîte de dialogue.
  2. On écoute les tabulations clavier.
  3. Si une tabulation est déclenchée lorsque le focus est sur le dernier élément, on déplace le focus sur le premier (et inversement en cas de navigation arrière).

Nous voilà avec un beau focus trap !

Sauf que…

On souhaitait désactiver la navigation clavier sur une partie du DOM ; et on l’a finalement piégée dans l’autre partie.

C’est presque pareil mais… Si on souhaite atteindre la barre d’adresse du navigateur, il n’est plus possible d’appuyer uniquement sur la tabulation pour ça. Heureusement, il existe d’autres raccourcis claviers dans les technologies d’assistance pour cela, mais on a quand même cassé quelque chose.

Une autre piste ?

L’autre approche connue, c’est de lister tous les éléments focusables en dehors de la boîte de dialogue et de les désactiver avec un tabindex négatif.

Ça fonctionne mieux et c’est plus logique, mais ça demande de ne rien oublier (ni à la désactivation, ni à la réactivation).
Et ça peut amener à modifier énormément de nœuds DOM (ce qui n’est jamais très bon signe).

La solution moderne

On cherche donc sur plusieurs éléments :

  • à désactiver les clics ;
  • à désactiver les focus ;
  • à supprimer leurs sémantiques ;
  • tout en conservant leurs apparences.

Il existe un nouvel attribut universel appelé inert qui fait tout ça d’un coup, et qui permet de le faire en ciblant directement le parent commun de tout les éléments que nous souhaitons désactiver.

Avec très peu de modifications du DOM et sans modifier le comportement natif de focus, nous avons le résultat souhaité !

Un autre cas pratique

Devinez ce qu’il serait possible de faire pour les éléments d’un carrousel qui ne sont pas visibles ?

Je pense que nous avons là une solution tout à fait adaptée, facilement combinée avec Intersection Observer par exemple.

Vincent.

0 commentaire.

Laisser un commentaire