Komponente mit Mehrfachauswahl erstellen

Eine grundlegende Übersicht zum Erstellen einer responsiven, adaptiven und zugänglichen Mehrfachauswahl-Komponente zum Sortieren und Filtern von Nutzererfahrungen.

In diesem Beitrag möchte ich Ihnen zeigen, wie Sie eine Komponente mit Mehrfachauswahl erstellen können. Demo ansehen

Demo

Falls du lieber ein Video hast, findest du hier eine YouTube-Version dieses Beitrags:

Überblick

Nutzern werden häufig viele Elemente präsentiert. In diesen Fällen kann es eine gute Idee sein, die Liste zu reduzieren, um eine Überlastung der Auswahlmöglichkeiten zu vermeiden. In diesem Blogpost geht es um die Filter-UI zur Reduzierung von Auswahlmöglichkeiten. Dazu werden Artikelattribute präsentiert, die Nutzer auswählen oder die Auswahl aufheben können. Dadurch werden die Ergebnisse reduziert und damit die Überlastung der Auswahlmöglichkeiten reduziert.

Interaktionen

Ziel ist es, für alle Nutzer und ihre unterschiedlichen Eingabetypen den schnellen Durchlauf der Filteroptionen zu ermöglichen. Dieses wird mit einem anpassungsfähigen und responsiven Komponentenpaar geliefert. Eine traditionelle Seitenleiste mit Kästchen für Computer, Tastaturen und Screenreader und ein <select multiple> für Touch-Nutzer.

Screenshot, der die hellen und dunklen Desktops mit Kästchen in der Seitenleiste im Vergleich zu iOS und Android mit Mehrfachauswahl zeigt

Diese Entscheidung, die integrierte Mehrfachauswahl für Touchscreens und nicht für Desktop-Geräte zu verwenden, spart Arbeit und schafft Arbeit, aber ich denke, dass eine angemessene Nutzererfahrung mit weniger Code-Verschulden entsteht, als die gesamte responsive Website in einer Komponente zu erstellen.

Berührung

Die Touch-Komponente spart Platz und verbessert die Genauigkeit der Nutzerinteraktion auf Mobilgeräten. Es spart Platz, indem eine gesamte Seitenleiste von Kästchen zu einer integrierten <select>-Overlay-Touchfunktion minimiert wird. Sie verbessert die Eingabegenauigkeit, da ein großes Touch-Overlay des Systems angezeigt wird.

Screenshot-Vorschau des Mehrfachauswahl-Elements in Chrome auf Android-Geräten, iPhones und iPads. Auf dem iPad und dem iPhone ist die Mehrfachauswahl aktiviert und bietet jeweils eine einzigartige Nutzererfahrung, die für die Bildschirmgröße optimiert ist.

Tastatur und Gamepad

Im Folgenden wird gezeigt, wie ein <select multiple> über die Tastatur verwendet wird.

Diese integrierte Mehrfachauswahl kann nicht gestaltet werden und wird nur in einem kompakten Layout angeboten, das sich nicht für viele Optionen eignet. Sehen Sie, dass Sie die Bandbreite der Optionen in diesem winzigen Feld nicht sehen können? Die Größe lässt sich zwar ändern, ist aber nicht so nützlich wie eine Seitenleiste mit Kästchen.

Markup

Beide Komponenten sind im selben <form>-Element enthalten. Die Ergebnisse dieses Formulars, egal ob Kästchen oder Mehrfachauswahl, werden beobachtet und zum Filtern des Rasters verwendet, können aber auch an einen Server gesendet werden.

<form>

</form>

Komponente „Kästchen“

Gruppen von Kästchen sollten in einem <fieldset>-Element umschlossen und mit <legend> versehen sein. Wenn HTML auf diese Weise strukturiert ist, verstehen Screenreader und FormData automatisch die Beziehung der Elemente.

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

Fügen Sie nach der Gruppierung <label> und <input type="checkbox"> für jeden der Filter hinzu. Ich habe mich dafür entschieden, die Mine in einem <div> einzubetten, damit die CSS-Eigenschaft gap sie gleichmäßig verteilen und die Ausrichtung beibehalten kann, wenn Labels mehrzeilig sind.

<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>

Ein Screenshot mit einem informativen Overlay für die Legenden- und Fieldset-Elemente sowie die Farbe und den Elementnamen.

<select multiple>-Komponente

Ein selten genutztes Feature des <select>-Elements ist multiple. Wenn das Attribut mit einem <select>-Element verwendet wird, können Nutzer mehrere Elemente aus der Liste auswählen. Es ist so, als würden Sie die Interaktion von einer Optionsfeldliste in eine Kästchenliste ändern.

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

Wenn Sie innerhalb einer <select> ein Label hinzufügen und Gruppen erstellen möchten, verwenden Sie das Element <optgroup> und weisen Sie ihm ein Attribut und einen Wert vom Typ label zu. Dieses Element und der Attributwert ähneln den Elementen <fieldset> und <legend>.

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

Fügen Sie jetzt die <option>-Elemente für den Filter hinzu.

<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>

Screenshot des Desktop-Renderings eines Elements mit Mehrfachauswahl

Eingabe mithilfe von Zählern nachverfolgen, um Hilfstechnologien zu informieren

Die Methode Statusrolle wird bei dieser Nutzererfahrung verwendet, um die Liste der Filter für Screenreader und andere Hilfstechnologien zu verfolgen und zu verwalten. Das YouTube-Video demonstriert diese Funktion. Die Integration beginnt mit HTML und dem Attribut role="status".

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

Durch dieses Element werden Änderungen am Inhalt vorgelesen. Wir können den Inhalt mit CSS-Zählern aktualisieren, wenn Nutzer mit den Kästchen interagieren. Dazu müssen wir zuerst einen Zähler mit einem Namen für ein übergeordnetes Element der Eingaben und des state-Elements erstellen.

aside {
  counter-reset: filters;
}

Standardmäßig ist die Anzahl 0, was toll ist. Standardmäßig ist in diesem Design nichts :checked.

Um den neu erstellten Zähler zu erhöhen, nehmen wir nun ein Targeting auf untergeordnete Elemente des <aside>-Elements an, die :checked sind. Wenn der Nutzer den Status der Eingaben ändert, zählt der filters-Zähler.

aside :checked {
  counter-increment: filters;
}

CSS erkennt jetzt die allgemeine Zählung der Kästchen-UI. Das Statusrollenelement ist leer und wartet auf Werte. Da CSS die Zählung im Arbeitsspeicher verwaltet, ermöglicht die Funktion counter() den Zugriff auf den Wert aus dem Inhalt des Pseudoelements:

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

Der HTML-Code für das Statusrollenelement sagt nun einem Screenreader „2 Filter“ an. Das ist ein guter Anfang, aber wir können es besser. Sie könnten z. B. die Liste der Ergebnisse teilen, die mit den Filtern aktualisiert wurden. Wir arbeiten über JavaScript, weil Zähler nicht so funktionieren.

Screenshot des macOS-Screenreaders mit der Anzahl der aktiven Filter.

Spannung pur

Der Zähleralgorithmus funktionierte gut mit CSS nesting-1, da ich die gesamte Logik in einem Block zusammenfassen konnte. Gerät ist handlich und zentral zum Lesen und Aktualisieren geeignet.

aside {
  counter-reset: filters;

  & :checked {
    counter-increment: filters;
  }

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

Layouts

In diesem Abschnitt werden die Layouts zwischen den beiden Komponenten beschrieben. Die meisten Layoutstile sind für die Komponente des Desktop-Kästchens vorgesehen.

Das Formular

Um die Lesbarkeit und Lesbarkeit für Nutzer zu optimieren, erhält das Formular eine maximale Breite von 30 Zeichen. Im Wesentlichen wird für jedes Filterlabel eine optische Linienbreite festgelegt. Das Formular verwendet das Rasterlayout und die Eigenschaft gap, um die Feldsätze aufzuteilen.

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

Das <select>-Element

Sowohl die Liste der Labels als auch die Kästchen belegen auf dem Mobilgerät zu viel Platz. Daher wird beim Layout geprüft, ob das primäre Zeigegerät des Nutzers angezeigt wird, um die Touchbedienung zu ändern.

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

Der Wert coarse bedeutet, dass der Nutzer mit seinem primären Eingabegerät nicht mit hoher Genauigkeit mit dem Bildschirm interagieren kann. Auf einem Mobilgerät ist der Zeigerwert häufig coarse, da die primäre Interaktion der Touch ist. Auf einem Desktop-Gerät ist der Zeigerwert häufig fine, da häufig eine Maus oder ein anderes Eingabegerät mit hoher Genauigkeit verbunden ist.

Feldsätze

Der Standardstil und das Standardlayout von <fieldset> mit <legend> sind eindeutig:

Screenshot der Standardstile für einen Feldsatz und eine Legende.

Normalerweise verwende ich die Eigenschaft gap, um meine untergeordneten Elemente zu platzieren. Aufgrund der einzigartigen Positionierung von <legend> ist es jedoch schwierig, eine Gruppe von untergeordneten Elementen mit gleichmäßigen Abständen zu erstellen. Anstelle von gap werden die Auswahl gleichgeordneter Elemente und margin-block-start verwendet.

fieldset {
  padding: 2ch;

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

Dadurch wird der Bereich von <legend> nicht mehr durch Targeting auf die untergeordneten <div> angepasst.

Screenshot mit dem Abstand zwischen den Eingaben, aber nicht der Legende.

Filterlabel und ‐kästchen

Als direktes untergeordnetes Element eines <fieldset>s und innerhalb der maximalen Breite des 30ch des Formulars wird der Labeltext möglicherweise umgebrochen, wenn er zu lang ist. Zeilenumbruch ist toll, aber Abstimmungsprobleme zwischen Text und Kästchen nicht. Flexbox ist dafür ideal.

fieldset > div {
  display: flex;
  gap: 2ch;
  align-items: baseline;
}
Screenshot, der zeigt, wie das Häkchen in einem mehrzeiligen Szenario an der ersten Textzeile ausgerichtet ist.
Weitere Spiele in diesem Codepen

Das animierte Raster

Die Layoutanimation erfolgt durch Isotope. Leistungsstarkes und leistungsstarkes Plug-in für interaktives Sortieren und Filtern.

JavaScript

JavaScript hilft nicht nur bei der Orchestrierung eines ansprechenden animierten, interaktiven Rasters, sondern auch, um einige Unregelmäßigkeiten auszubessern.

Nutzereingabe normalisieren

Dieses Design hat ein Formular mit zwei verschiedenen Möglichkeiten zur Bereitstellung von Eingaben. Es wird nicht serialisiert. Mit etwas JavaScript können wir die Daten normalisieren.

Screenshot der JavaScript-Konsole der Entwicklertools mit den normalisierten Datenergebnissen des Ziels

Ich habe mich dafür entschieden, die Datenstruktur des <select>-Elements an der gruppierten Kästchenstruktur auszurichten. Dazu wird dem Element <select> ein input-Ereignis-Listener hinzugefügt. Dann wird selectedOptions zugeordnet.

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
  }, [])
})

Jetzt können Sie das Formular senden oder Isotope im Fall dieser Demo anweisen, wonach gefiltert werden soll.

Fertigstellen des Statusrollenelements

Das Element zählt nur die Filteranzahl basierend auf der Interaktion mit Kästchen, aber ich hielt es für eine gute Idee, zusätzlich die Anzahl der Ergebnisse anzugeben und dafür zu sorgen, dass auch die Auswahlmöglichkeiten für <select>-Elemente berücksichtigt werden.

<select> Elementauswahl, die in counter() widergespiegelt wird

Im Abschnitt zur Datennormalisierung wurde bei der Eingabe bereits ein Listener erstellt. Am Ende dieser Funktion sind die Anzahl der ausgewählten Filter und die Anzahl der Ergebnisse für diese Filter bekannt. Die Werte können so an das Statusrollenelement übergeben werden.

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

Im role="status"-Element widergespiegelte Ergebnisse

Mit :checked kann die Anzahl der ausgewählten Filter an das Statusrollenelement übergeben werden. Die Anzahl der gefilterten Ergebnisse ist jedoch nicht sichtbar. JavaScript kann Interaktionen mit den Kästchen beobachten und nach dem Filtern des Rasters textContent hinzufügen, wie es das <select>-Element getan hat.

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

Insgesamt ist damit die Ankündigung „2 Filter ergeben 25 Ergebnisse“ abgeschlossen.

Screenshot des macOS-Screenreaders, der die Ergebnisse ankündigt.

Jetzt steht unsere hervorragende Erfahrung mit assistiven Technologien allen Nutzenden zur Verfügung, egal, wie sie damit interagieren.

Fazit

Jetzt weißt du, wie ich es gemacht habe. Wie würdest du es erreichen? 🙂

Diversifizieren wir unsere Ansätze und lernen Sie alle Möglichkeiten kennen, wie wir das Web nutzen können. Erstelle eine Demo und twittere mich über Links, und ich füge sie unten zum Abschnitt über Community-Remixe hinzu.

Community-Remixe

Hier gibt es noch nichts zu sehen.