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
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.
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.
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>
<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>
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.
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:
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.
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.](https://web.dev/static/articles/building/a-multi-select-component/image/screenshot-showing-the-c-b135285ef7d8a.png?authuser=6&hl=de)
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.
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.
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.