Créer un composant à sélection multiple

Présentation générale de la création d'un composant multi-sélection responsif, adaptatif et accessible pour trier et filtrer les expériences utilisateur.

Dans ce message, je vais vous expliquer comment créer un composant à sélection multiple. Essayez le demo.

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">
</ph> Démonstration

Si vous préférez la vidéo, voici une version YouTube de cet article:

Présentation

Les utilisateurs voient souvent des éléments, parfois beaucoup d'éléments. Dans ces dans certains cas, il peut être judicieux de proposer un moyen de réduire la liste pour éviter la surcharge de choix. Ce article de blog explore le filtrage de l'interface utilisateur comme moyen de réduire les choix. Pour ce faire, Présenter des attributs d'article que les utilisateurs peuvent sélectionner ou désélectionner, ce qui réduit les résultats ce qui réduit la surcharge de choix.

Interactions

L'objectif est de permettre un balayage rapide des options de filtrage pour tous les utilisateurs et leurs différents types d'entrées. Pour cela, elle devra adopter une approche une paire de composants. Barre latérale traditionnelle contenant des cases à cocher pour le bureau, le clavier et lecteurs d'écran, ainsi qu'un élément <select multiple> pour les utilisateurs tactiles.

Capture d&#39;écran de comparaison montrant un bureau clair et sombre avec une barre latérale
cases à cocher par rapport aux mobiles iOS et Android avec un élément multi-sélection.

La décision d'utiliser la fonctionnalité de sélection multiple intégrée pour le toucher, et non pour les ordinateurs de bureau, permet d'économiser du travail et crée du travail, mais je pense qu'elle offre des expériences appropriées qui nécessitent moins de code que la création de l'ensemble de l'expérience responsive en un seul composant.

Toucher

Le composant tactile permet de gagner de l'espace et d'améliorer la précision des interactions de l'utilisateur sur mobile. Elle permet de gagner de la place en réduisant toute une barre latérale de cases à cocher dans un Expérience tactile en superposition <select> intégrée. Il contribue à la précision des entrées en montrant une superposition tactile de grande taille fournie par le système.

A
Capture d&#39;écran de l&#39;élément multi-sélection dans Chrome sur Android, iPhone et
iPad. Sur l&#39;iPad et l&#39;iPhone, la sélection multiple est activée, et chacun a une
une expérience unique,
optimisée pour la taille de l&#39;écran.

Clavier et manette de jeu

Vous trouverez ci-dessous une démonstration de l'utilisation d'un élément <select multiple> à partir du clavier.

Cette sélection multiple intégrée ne peut pas être stylisée et n'est proposée que dans un format compact mise en page non adaptée à la présentation d'un grand nombre d'options. Découvrez comment vous ne pouvez pas voir l’étendue des options dans cette petite boîte ? Vous pouvez modifier sa taille, toujours pas aussi utilisables qu'une barre latérale de cases à cocher.

Majoration

Les deux composants seront contenus dans le même élément <form>. Les résultats de ce formulaire, qu'il s'agisse de cases à cocher ou de sélections multiples, sera observé et utilisé pour filtrer la grille, mais pourrait également être envoyé à un serveur.

<form>

</form>

Composant de cases à cocher

Les groupes de cases à cocher doivent être placés dans <fieldset> auquel on attribue une valeur <legend> Lorsque le code HTML est structuré de cette manière, les lecteurs d'écran et FormData va comprendre automatiquement la relation entre les éléments.

<form>
  <fieldset>
    <legend>New</legend>
    … checkboxes …
  </fieldset>
</form>

Une fois le regroupement en place, ajoutez un <label> et un <input type="checkbox"> pour chacun des filtres. J'ai choisi d'encapsuler le mien dans un <div> afin que la propriété CSS gap vous pouvez les espacer uniformément et maintenir l'alignement lorsque les libellés sont multilignes.

<form>
  <fieldset>
    <legend>New</legend>
    <div>
      <input type="checkbox" id="last 30 days" name="new" value="last 30 days">
      <label for="last 30 days">Last 30 Days</label>
    </div>
    <div>
      <input type="checkbox" id="last 6 months" name="new" value="last 6 months">
      <label for="last 6 months">Last 6 Months</label>
    </div>
   </fieldset>
</form>

Une capture d&#39;écran avec une superposition informative sur la légende et
  affiche la couleur et le nom de l&#39;élément.

Composant <select multiple>

Une caractéristique rarement utilisée de l'élément <select> est multiple Lorsque l'attribut est utilisé avec un élément <select>, l'utilisateur est autorisé à choisissez-en plusieurs dans la liste. C'est comme changer l'interaction d'une liste d'options à une liste de cases à cocher.

<form>
  <select multiple="true" title="Filter results by category">
    …
  </select>
</form>

Pour ajouter un libellé et créer des groupes dans un <select>, utilisez la méthode <optgroup> et attribuez-lui un attribut et une valeur label. Cet élément et cet attribut sont semblables aux éléments <fieldset> et <legend>.

<form>
  <select multiple="true" title="Filter results by category">
    <optgroup label="New">
      …
    </optgroup>
  </select>
</form>

À présent, ajoutez le <option> pour le filtre.

<form>
  <select multiple="true" title="Filter results by category">
    <optgroup label="New">
      <option value="last 30 days">Last 30 Days</option>
      <option value="last 6 months">Last 6 Months</option>
    </optgroup>
  </select>
</form>

Capture d&#39;écran d&#39;un élément multi-sélection, sur ordinateur.

Suivi des entrées avec des compteurs pour informer les technologies d'assistance

L'état rôle est utilisée dans cette expérience utilisateur, pour suivre et maintenir le décompte des filtres pour les lecteurs d'écran et d'autres technologies d'assistance. Vidéo YouTube présente la fonctionnalité. L'intégration commence par le code HTML et l'attribut role="status"

<div role="status" class="sr-only" id="applied-filters"></div>

Cet élément lit à voix haute les modifications apportées au contenu. Nous pouvons mettre à jour contenu avec CSS compteurs lorsque les utilisateurs interagissent avec les cases à cocher. Pour ce faire, nous devons d'abord créer compteur avec un nom sur un élément parent des entrées et de l'élément d'état.

aside {
  counter-reset: filters;
}

Par défaut, le nombre est de 0, ce qui est très bien, car aucun résultat n'indique :checked par défaut dans cette conception.

Ensuite, pour incrémenter le compteur que vous venez de créer, nous allons cibler les enfants du Élément <aside> défini sur :checked. Lorsque l'utilisateur modifie l'état des entrées, le compteur filters va être comptabilisé.

aside :checked {
  counter-increment: filters;
}

Le CSS connaît désormais le décompte général de l'interface utilisateur de la case à cocher et le rôle d'état est vide et attend des valeurs. Comme le CSS gère le décompte mémoire, le counter() permet d'accéder à la valeur d'une valeur pseudo- :

aside #applied-filters::before {
  content: counter(filters) " filters ";
}

Le code HTML de l'élément de rôle d'état affichera désormais "2 filtres" à un écran en lecture seule. C'est un bon début, mais nous pouvons faire mieux, par exemple partager le décompte les résultats que les filtres ont mis à jour. Nous effectuerons cette tâche à partir de JavaScript, en dehors de ce que les compteurs peuvent faire.

Capture d&#39;écran du lecteur d&#39;écran macOS indiquant le nombre de filtres actifs.

Excitation liée à l'imbrication

L'algorithme des compteurs était optimal avec CSS nesting-1, car j'ai pu insérer toutes les dans un seul bloc. Se sent portable et centralisée pour la lecture et la mise à jour.

aside {
  counter-reset: filters;

  & :checked {
    counter-increment: filters;
  }

  & #applied-filters::before {
    content: counter(filters) " filters ";
  }
}

Mises en page

Cette section décrit les mises en page entre les deux composants. La plupart des les styles de mise en page s'appliquent au composant de case à cocher pour ordinateur de bureau.

Le formulaire

Afin d'optimiser la lisibilité et la lisibilité du formulaire, le formulaire reçoit une valeur maximale de 30 caractères, soit une épaisseur de ligne optique libellé de filtre. Le formulaire utilise une mise en page sous forme de grille et la propriété gap pour espacer les des ensembles de champs.

form {
  display: grid;
  gap: 2ch;
  max-inline-size: 30ch;
}

L'élément <select>

La liste des libellés et des cases à cocher occupe trop d'espace sur les appareils mobiles. Par conséquent, la mise en page vérifie que le principal dispositif de pointage de l'utilisateur à changer l'expérience tactile.

@media (pointer: coarse) {
  select[multiple] {
    display: block;
  }
}

La valeur coarse indique que l'utilisateur ne pourra pas interagir avec l'écran avec une grande précision avec son périphérique d'entrée principal. Sur un appareil mobile, la valeur du pointeur est souvent coarse, car il s'agit de l'interaction principale c'est le toucher. Sur un ordinateur de bureau, la valeur du pointeur est souvent fine, comme c'est souvent le cas. d’avoir une souris ou un autre périphérique d’entrée haute précision connecté.

Ensembles de champs

Le style et la mise en page par défaut d'un <fieldset> avec un <legend> sont uniques:

Capture d&#39;écran des styles par défaut pour un ensemble de champs et une légende.

Normalement, pour espacer mes éléments enfants, j'utilise la propriété gap, mais la valeur unique le positionnement de <legend> rend difficile la création d'un ensemble espacé uniformément. des enfants. Au lieu de gap, le sœur adjacent sélecteur et margin-block-start sont utilisés.

fieldset {
  padding: 2ch;

  & > div + div {
    margin-block-start: 2ch;
  }
}

Cela évite que <legend> n'ait un ajustement de son espace en ne ciblant que le <div> enfant.

Capture d&#39;écran montrant l&#39;espacement des marges entre les entrées, mais pas la légende.

Le libellé et la case à cocher du filtre

En tant qu'enfant direct d'un élément <fieldset>, dans la largeur maximale de la 30ch, le texte du libellé risque d'être renvoyé à la ligne s'il est trop long. Le retour à la ligne automatique est une bonne chose, mais un désalignement entre le texte et la case à cocher ne l’est pas. Flexbox est la solution idéale pour cela.

fieldset > div {
  display: flex;
  gap: 2ch;
  align-items: baseline;
}
<ph type="x-smartling-placeholder">
</ph> Capture d&#39;écran montrant l&#39;alignement de la coche
    la première ligne de texte dans un scénario
de retour à la ligne automatique sur plusieurs lignes.
Plus d'infos dans Codepen

La grille animée

L'animation de mise en page est réalisée par Isotope. A performant et performant pour le tri et le filtrage interactifs.

JavaScript

En plus de vous aider à orchestrer une grille interactive et animée, JavaScript sert à polir quelques arêtes.

Normaliser l'entrée utilisateur

Cette conception a une forme avec deux façons différentes de fournir des informations, et elles ne pas sérialiser même chose. Mais avec un peu de JavaScript, normaliser les données.

Capture d&#39;écran de la console JavaScript DevTools, qui
  affiche l&#39;objectif, les résultats
des données normalisées.

J'ai choisi d'aligner la structure de données de l'élément <select> sur les cases à cocher groupées. structure. Pour ce faire, un input l'écouteur d'événements est ajouté à l'élément <select>. Il est alors selectedOptions sont mappées.

document.querySelector('select').addEventListener('input', event => {
  // make selectedOptions iterable then reduce a new array object
  let selectData = Array.from(event.target.selectedOptions).reduce((data, opt) => {
    // parent optgroup label and option value are added to the reduce aggregator
    data.push([opt.parentElement.label.toLowerCase(), opt.value])
    return data
  }, [])
})

Vous pouvez maintenant envoyer le formulaire en toute sécurité ou, dans le cas de cette démonstration, demander à Isotope sur les éléments à filtrer.

Finalisation de l'élément de rôle d'état

L'élément comptabilise et annonce uniquement le nombre de filtres en fonction d'une case à cocher interaction, mais j'ai pensé que c'était une bonne idée de partager également le nombre de et vous assurer que les choix d'éléments <select> sont également comptés.

Le choix de l'élément <select> est reflété dans counter()

Dans la section de normalisation des données, un écouteur a déjà été créé pour l'entrée. À la fin de cette fonction, le nombre de filtres choisis et le nombre de résultats pour ces filtres sont connues. Les valeurs peuvent être transmises à l'élément du rôle d'état comme ceci.

let statusRoleElement = document.querySelector('#applied-filters')
statusRoleElement.style.counterSet = selectData.length

Résultats reflétés dans l'élément role="status"

:checked fournit un moyen intégré de transmettre le nombre de filtres choisis à l'élément du rôle d'état, mais n'a pas accès au nombre de résultats filtrés. JavaScript peut surveiller l'interaction avec les cases à cocher et après avoir filtré grille, ajoutez textContent comme l'élément <select>.

document
  .querySelector('aside form')
  .addEventListener('input', e => {
    // isotope demo code
    let filterResults = IsotopeGrid.getFilteredItemElements().length
    document.querySelector('#applied-filters').textContent = `giving ${filterResults} results`
})

Au total, ce travail achève l'annonce "2 filtres donnant 25 résultats".

Capture d&#39;écran du lecteur d&#39;écran macOS annonçant les résultats.

Notre excellente expérience technologique d'assistance sera désormais assurée les utilisateurs, quelle que soit leur façon d'interagir avec elle.

Conclusion

Maintenant que vous savez comment j'ai fait, comment feriez-vous ? 😃

Diversifiez nos approches et découvrons toutes les manières de créer des applications sur le Web. Créer une démonstration, me envoyer des tweets et je l'ajouterai à la section des remix de la communauté ci-dessous.

Remix de la communauté

Rien à afficher pour le moment !