Quand un bouton n'est-il pas un bouton ?
Publié: 2022-03-10Disons que vous avez une partie d'une interface sur laquelle l'utilisateur clique et que quelque chose se passe. Cela ressemble à un bouton pour moi, mais appelons cela une "chose de clic" pour l'instant. Je sais, vous êtes convaincu que c'est aussi un bouton : il est arrondi et se distingue par une belle couleur tomate, demandant à interagir avec. Mais réfléchissons un instant. Cela vous fera gagner du temps à long terme, je vous le promets.
Et si le texte de cette chose cliquable était "En savoir plus", et qu'en cliquant dessus, l'utilisateur était dirigé vers un article sur une autre page ? Hmm. Et s'il y avait un mot bleu souligné, "Fermer", qui ferme la boîte de dialogue contextuelle ? Est-ce un lien simplement parce qu'il est bleu et souligné ? Bien sûr que non.
Waouh ! Il semble qu'il n'y ait aucun moyen de savoir s'il s'agit d'un lien ou d'un bouton simplement en le regardant. C'est fou! Nous devons comprendre ce que fait cette chose avant de choisir le bon élément. Mais que se passe-t-il si nous ne savons pas encore ce qu'il fait ou si nous sommes simplement confus ? Eh bien, il y a un organigramme pratique pour nous :
- C'est un bouton.
- Si ce n'est pas le cas, c'est un lien.
- C'est ça.
Alors, est-ce que tout est un bouton ? Non, mais vous pouvez toujours commencer avec un bouton pour presque tous les éléments sur lesquels vous pouvez cliquer ou interagir de la même manière. Et s'il manque quelque chose, comme la navigation vers une autre page, utilisez plutôt un lien. Et non, un pointeur n'est pas une raison pour le faire <a href>
. Nous avons cursor: pointer
pour cela.
D'accord, c'est un <button>
— nous sommes d'accord là-dessus. Mettons-le dans notre modèle et stylisons-le en fonction de la conception : du rembourrage, des arrondis, un remplissage de tomate, du texte blanc et même des styles de mise au point. Oh, c'est tellement gentil de ta part.
<button type="button" class="button"> Something </button> <style> .button { display: inline-block; padding: 10px 20px; border-radius: 20px; background-color: tomato; color: white; } .button:focus { outline: none; box-shadow: 0 0 0 5px #006AE3; } </style>
Cela n'a pas pris longtemps. Vous vouliez le construire rapidement et prendre un déjeuner parce que vous avez faim. Ok, voyons à quoi ça ressemble et allons-y.
Oh mon Dieu! Quelque chose ne va pas avec le navigateur. Pourquoi ce bouton est-il si laid ? Le texte est minuscule, même si nous avons explicitement défini le body
sur 16px
, et même la font-family
est erronée. La bordure arrondie avec une pseudo-ombre idiote est tellement rétro que ce n'est même pas encore une tendance.
Ahh, c'est le style par défaut du navigateur. Vous devez l'annuler avec précaution ou même ajouter Normalize.css ou Reset.css… ou vous pouvez simplement utiliser un <div>
et l'oublier. La résolution rapide des problèmes n'est-elle pas ce pour quoi ils vous paient ? Vous avez faim et cela ne vous aide pas du tout. Mais vous êtes un professionnel : ressaisissez-vous et réfléchissez.
Quelle est la différence entre un <button>
et un <div>
de toute façon ? Un <button>
intégré est un élément interactif, ce qui signifie qu'il est possible d'interagir avec lui. C'est profond. Vous pouvez cliquer dessus, vous pouvez vous concentrer dessus à l'aide d'un clavier, et il transmet également un rôle de button
accessible aux lecteurs d'écran, permettant aux utilisateurs de comprendre qu'il s'agit d'un bouton.
Impressionnant! Vous n'êtes pas seulement au courant de l'élément <button>
de HTML, mais vous connaissez également une ou deux choses sur ARIA et la prise en charge des lecteurs d'écran. Vous avez peut-être même essayé VoiceOver ou NVDA pour tester l'accessibilité de vos interfaces.
Donc, vous avez décidé de faire un tour. Vous ne jouerez pas avec le style du navigateur et vous ferez en sorte que l'élément ressemble à un bouton interactif approprié pour les utilisateurs qui pourraient en avoir besoin. C'est intelligent !
<div class="button" tabindex="0" role="button"> Something </div>
Maintenant, non seulement il a l'air correct, mais il est focalisable via le clavier grâce à l' tabindex="0"
, et les lecteurs d'écran le traiteront comme un bouton approprié car vous y avez judicieusement ajouté role="button"
. Git commit && push alors ! Il y a quelques tâches supplémentaires pour cette chose, mais nous en avons fini avec le style. Qu'est ce qui pourrait aller mal? Il est l'heure de déjeuner. Super, allons-y !
Une heure plus tard…
C'était un bon déjeuner ! Revenons à notre truc cliquable. Nous devons terminer certaines tâches avant de continuer. Voyons voir… Nous devons appeler une fonction doSomething
une fois que le bouton est cliqué, et il devrait y avoir un moyen de désactiver le bouton pour qu'il ne soit pas cliquable. Cela semble facile. Ajoutons un écouteur d'événement à ce bouton :
<script> const buttons = document.querySelectorAll('.button'); [...buttons].forEach(button => { button.addEventListener('click', doSomething); }); function doSomething() { console.log('Something!'); } </script>
Terminé. L'utilisateur peut maintenant cliquer dessus avec une souris sur le bureau et appuyer dessus avec un doigt sur un écran tactile. Un événement de clic se déclenchera de manière fiable et vous verrez beaucoup de Quelque chose ! dans votre console. Quelle est la prochaine tâche ?
Attendez! Nous devons nous assurer que cela fonctionne de la même manière pour les utilisateurs de clavier. Parce que nous avons ce tabindex="0"
sur le bouton, il peut être focalisé, et une fois qu'il est focalisé, les utilisateurs devraient pouvoir appuyer sur la barre d'espace ou la touche "Entrée" pour déclencher tout ce que nous avons attaché.
Nous devons donc attacher un autre écouteur d'événement pour capter toutes les touches, et nous déclencherons notre fonction uniquement pour certaines touches. Dieu merci, les appareils tactiles sont suffisamment intelligents pour convertir tous les tapotements en clics ; sinon, nous devrions également attacher un tas d'événements tactiles.
<script> const buttons = document.querySelectorAll('.button'); [...buttons].forEach(button => { button.addEventListener('click', doSomething); button.addEventListener('keyup', (event) => { if (event.key == 'Enter' || event.key == ' ') { doSomething(); } }); }); function doSomething() { console.log('Something!'); } </script>
Phew! Maintenant, notre truc cliquable est entièrement accessible depuis le clavier. Je suis si fier de toi! Et JavaScript est vraiment magique - que ferions-nous sans lui ?
D'accord, quelle est la dernière tâche : "Le bouton doit avoir un état désactivé qui change son apparence et son comportement en quelque chose d'engourdi." Engourdi? Je suppose que cela signifie quelque chose de gris et ne répond pas à l'interaction. OK, ajoutons un état dans la feuille de style en utilisant le nommage BEM.
<div class="button button--disabled" tabindex="0" role="button"> Something </div> <style> .button--disabled { background-color: #9B9B9B; } </style>
Cela m'a l'air confortablement engourdi . Chaque fois que le bouton doit être désactivé, nous ajouterons le modificateur button--disabled
pour le rendre gris. Mais ce n'est pas encore assez engourdi : il peut toujours être focalisé et déclenché à la fois par un pointeur et depuis le clavier.
Merde, ça devient délicat.
Non seulement cela, mais le bouton ne doit pas être accessible dans l'ordre de tabulation, ce qui signifie que l'attribut tabindex
ne doit pas être là. Et nous devons vérifier si le bouton a l'état désactivé, puis arrêter de déclencher notre fonction. De plus, ce modificateur pourrait être appliqué dynamiquement. Bien que ce ne soit pas un problème pour CSS de faire correspondre des éléments avec des sélecteurs à la volée et d'appliquer des styles, nous pourrions avoir besoin d'une sorte d'observateur de mutation pour déclencher d'autres changements pour ce bouton.
N'est-ce pas? Nous pensions que ce serait un simple petit bouton qui déclenche une fonction et a un état désactivé. Nous avons essayé d'arranger les choses avec l'accessibilité et tout ça, et maintenant nous sommes plongés dans ce terrier de lapin.
Prenons de la nourriture à emporter. Nous ne serons pas à la maison pour le dîner lorsque nous aurons terminé et testé correctement cela. Sacré W3C ! Pourquoi n'essayent-ils pas de nous faciliter la vie ? Comme s'ils se souciaient de nous !
En fait, ils font…
Faisons quelques pas en arrière avant de sauter dans ce pétrin. Pourquoi n'essayons-nous pas de faire ces choses en utilisant l'élément <button>
? Il a quelques astuces utiles dans sa manche, pas seulement les styles laids du navigateur. Oh, et n'oubliez pas type="button"
- vous ne voulez pas que le bouton "Fermer" de la fenêtre contextuelle soumette accidentellement le formulaire, car type="submit"
est la valeur par défaut.
Apparemment, lorsque le <button>
est focalisé et que la barre d'espace ou la touche "Entrée" est enfoncée, cela déclenchera l'événement de click
, tout comme les appareils mobiles le font lorsqu'ils reçoivent des tapotements, des tapotements, des coups de langue ou tout ce qu'ils sont capables de recevoir aujourd'hui. Un écouteur d'événement de moins dans notre code ! Agréable.
// A click is enough! button.addEventListener('click', doSomething);
Comme pour l'état désactivé, l'attribut disabled
est disponible pour l'élément <button>
, ainsi que pour tous les éléments de formulaire, y compris <fieldset>
. Sans blague. Saviez-vous que vous pouvez désactiver tout un tas d'entrées regroupées simplement en appliquant un seul attribut au parent <fieldset>
?
<fieldset disabled> <legend>A bunch of numb inputs</legend> <p> <label> <input type="radio" name="option"> Of course it's a link </label> </p> <p> <label> <input type="radio" name="option"> Obviously, it's a button </label> </p> <p> <label> <input type="radio" name="option"> I just wanna go home </label> </p> <button type="button">Button</button> </fieldset>
Maintenant tu sais! Cet attribut ne se contente pas de désactiver tous les événements sur les éléments de formulaire, mais les supprime également de l'ordre de tabulation. Problème résolu!
<button disabled type="button" class="button"> Something </button>
Mais attendez, il y a plus ! Il déclenche également la pseudo-classe :disabled
dans CSS, ce qui signifie que nous pouvons nous débarrasser du modificateur BEM pour déclarer des styles et utiliser le modificateur dynamique intégré à la place.
.button:disabled { background-color: #9B9B9B; }
En ce qui concerne les styles laids du navigateur, nous n'avons pas besoin d'utiliser tout Normalize.css pour réparer un seul bouton. Utilisez-le comme une source de sagesse : les trois lignes supplémentaires ci-dessous résoudront la plupart des différences ennuyeuses par rapport au <div>
. Si jamais vous en avez besoin de plus, vous pouvez en copier les parties pertinentes.
.button { font-size: 100%; font-family: inherit; border: none; }
Terminé. Le HTML n'est pas si mal après tout !
Mais si cela vous surprend de temps en temps, assurez-vous de vérifier la spécification HTML pour obtenir des réponses. Il est devenu beaucoup plus convivial au fil des ans et regorge de bons exemples d'utilisation et d'accessibilité. Et, bien sûr, le bon vieux HTML5 Doctor est toujours un endroit fiable pour comprendre la différence entre les éléments <section>
et <article>
et pour vérifier si le plan du document est encore une chose (pas vraiment). Il y a de fortes chances que vous finissiez également par lire la documentation HTML de Mozilla, et vous ne le regretterez pas non plus.
Cette tâche est maintenant terminée ! Et après? Un calendrier carrousel déroulant avec un champ de recherche ? Oh mon! Bonne chance avec ça. Mais rappelez-vous : le <button>
est votre ami !
Lectures complémentaires sur SmashingMag :
- Comment concevoir de meilleurs boutons
- Construire des boutons bascule inclusifs
- Conception de boutons fantômes : est-ce vraiment toujours une chose (et pourquoi) ?
- Modèles de conception : quand enfreindre les règles est acceptable