Eine grundlegende Übersicht dazu, wie Sie eine responsive, adaptive und barrierefreie Mehrfachauswahlkomponente zum Sortieren und Filtern von Inhalten erstellen.
In diesem Beitrag möchte ich meine Gedanken zu einer Möglichkeit zur Erstellung einer Mehrfachauswahlkomponente teilen. Demo ansehen
Wenn du lieber ein Video ansiehst, findest du hier eine YouTube-Version dieses Beitrags:
Übersicht
Nutzern werden oft Elemente angezeigt, manchmal sehr viele. In diesen Fällen kann es sinnvoll sein, eine Möglichkeit zur Reduzierung der Liste anzubieten, um eine Überforderung durch zu viele Optionen zu vermeiden. In diesem Blogpost wird die Filteroberfläche als Möglichkeit zur Reduzierung der Auswahlmöglichkeiten untersucht. Dazu werden Artikelattribute angezeigt, die Nutzer auswählen oder deaktivieren können. So werden die Ergebnisse reduziert und die Auswahl übersichtlicher.
Interaktionen
Ziel ist es, allen Nutzern und ihren unterschiedlichen Eingabetypen eine schnelle Navigation durch die Filteroptionen zu ermöglichen. Dazu werden zwei anpassbare und responsive Komponenten verwendet. Eine traditionelle Seitenleiste mit Kästchen für Desktop-Computer, Tastaturen und Screenreader sowie ein <select
multiple>
für Touchbildschirme.
Die Entscheidung, die integrierte Mehrfachauswahl für Touchbedienung und nicht für Desktop-Computer zu verwenden, spart Arbeit und schafft Arbeit. Ich glaube jedoch, dass sie eine angemessene Benutzererfahrung mit weniger Code-Schulden bietet, als die gesamte responsive Benutzeroberfläche in einer Komponente zu erstellen.
Berührung
Die Touch-Komponente spart Platz und trägt dazu bei, die Genauigkeit der Nutzerinteraktion auf Mobilgeräten zu verbessern. So wird Platz gespart, da eine ganze Seitenleiste mit Kästchen in ein integriertes<select>
Touch-Overlay minimiert wird. Sie trägt zur Eingabegenauigkeit bei, da das System ein großes Touch-Overlay anzeigt.
Tastatur und Gamepad
Unten sehen Sie eine Demonstration, wie Sie <select multiple>
über die Tastatur verwenden.
Diese integrierte Mehrfachauswahl kann nicht formatiert werden und ist nur in einem kompakten Layout verfügbar, das sich nicht für die Darstellung vieler Optionen eignet. Sehen Sie, wie Sie in diesem winzigen Feld nicht wirklich die Vielfalt der Optionen sehen können? Sie können die Größe zwar ändern, aber sie ist immer noch nicht so nutzerfreundlich wie eine Seitenleiste mit Kästchen.
Markieren & Zeichnen
Beide Komponenten befinden sich im selben <form>
-Element. Die Ergebnisse dieses Formulars, 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 eingeschlossen und mit einem <legend>
versehen werden.
Wenn HTML so strukturiert ist, können Screenreader und FormData die Beziehung der Elemente automatisch erkennen.
<form>
<fieldset>
<legend>New</legend>
… checkboxes …
</fieldset>
</form>
Fügen Sie nach der Gruppierung für jeden Filter ein <label>
und ein <input type="checkbox">
hinzu. Ich habe meine in einen <div>
-Block gepackt, damit die CSS-Eigenschaft gap
sie gleichmäßig platzieren 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
Eine selten verwendete Funktion des <select>
-Elements ist multiple
.
Wenn das Attribut mit einem <select>
-Element verwendet wird, kann der Nutzer mehrere Elemente aus der Liste auswählen. Das ist vergleichbar mit dem Wechsel von einer Auswahlliste zu einer Kästchenliste.
<form>
<select multiple="true" title="Filter results by category">
…
</select>
</form>
Wenn du innerhalb eines <select>
-Elements Gruppen labeln und erstellen möchtest, verwende das Element <optgroup>
und gib ihm ein label
-Attribut und einen label
-Wert. Dieses Element und dieses Attribut ähneln den Elementen <fieldset>
und <legend>
.
<form>
<select multiple="true" title="Filter results by category">
<optgroup label="New">
…
</optgroup>
</select>
</form>
Fügen Sie nun 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>
Eingaben mit Zählern erfassen, um Hilfstechnologien zu informieren
Bei dieser Nutzererfahrung wird die Statusrolle verwendet, um die Anzahl der Filter für Screenreader und andere Hilfstechnologien zu erfassen und zu verwalten. In diesem YouTube-Video wird die Funktion veranschaulicht. Die Integration beginnt mit HTML und dem Attribut role="status"
.
<div role="status" class="sr-only" id="applied-filters"></div>
Mit diesem 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 in einem übergeordneten Element der Eingaben und des Statuselements erstellen.
aside {
counter-reset: filters;
}
Standardmäßig ist die Anzahl 0
. Das ist gut, da in diesem Design standardmäßig nichts :checked
ist.
Als Nächstes richten wir das Ziel auf untergeordnete Elemente des Elements <aside>
aus, die :checked
sind, um den neu erstellten Zähler zu erhöhen. Wenn der Nutzer den Status der Eingaben ändert, wird der filters
-Zähler erhöht.
aside :checked {
counter-increment: filters;
}
CSS kennt jetzt die allgemeine Gesamtzahl der Kästchen-UI und das Status-Rollenelement ist leer und wartet auf Werte. Da die Zählung in CSS im Arbeitsspeicher gespeichert wird, kann über die Funktion counter()
auf den Wert aus dem Inhalt des Pseudoelements zugegriffen werden:
aside #applied-filters::before {
content: counter(filters) " filters ";
}
Die HTML-Datei für das Statuselement für die Rolle liest jetzt „2 Filter“ für einen Screenreader vor. Das ist ein guter Anfang, aber wir können noch besser werden. Zum Beispiel könnten wir die Anzahl der Ergebnisse anzeigen, die durch die Filter aktualisiert wurden. Wir führen diese Arbeit in JavaScript aus, da Zähler dafür nicht geeignet sind.
Nesting-Aufregung
Der Zähleralgorithmus funktionierte mit CSS-Verschachtelung 1 hervorragend, da ich die gesamte Logik in einen Block einfügen konnte. Sie ist portabel und zentralisiert für das Lesen und Aktualisieren.
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 Kästchenkomponente auf dem Computer gedacht.
Das Formular
Um die Lesbarkeit und Übersichtlichkeit für Nutzer zu optimieren, hat das Formular eine maximale Breite von 30 Zeichen. Damit wird im Grunde eine optische Zeilenbreite für jedes Filterlabel festgelegt. Im Formular wird das Rasterlayout und das Attribut gap
verwendet, um die Feldgruppen zu verteilen.
form {
display: grid;
gap: 2ch;
max-inline-size: 30ch;
}
Das <select>
-Element
Die Liste der Labels und Kästchen belegen auf Mobilgeräten zu viel Platz. Daher wird im Layout das primäre Eingabegerät des Nutzers geprüft, um die Oberfläche für Touchbedienung anzupassen.
@media (pointer: coarse) {
select[multiple] {
display: block;
}
}
Ein Wert von coarse
gibt an, dass der Nutzer mit seinem primären Eingabegerät nicht sehr präzise mit dem Display interagieren kann. Auf einem Mobilgerät ist der Zeigerwert oft coarse
, da die primäre Interaktion das Tippen ist. Auf einem Desktop-Gerät ist der Cursorwert häufig fine
, da in der Regel eine Maus oder ein anderes Eingabegerät mit hoher Präzision angeschlossen ist.
Die fieldsets
Das Standard-Styling und -Layout eines <fieldset>
mit einem <legend>
ist einzigartig:
Normalerweise verwende ich die Eigenschaft gap
, um die Abstände zwischen meinen untergeordneten Elementen zu steuern. Aufgrund der einzigartigen Positionierung des <legend>
ist es jedoch schwierig, eine gleichmäßig verteilte Gruppe von untergeordneten Elementen zu erstellen. Anstelle von gap
werden die adjacent sibling-Auswahl und margin-block-start
verwendet.
fieldset {
padding: 2ch;
& > div + div {
margin-block-start: 2ch;
}
}
Dadurch wird verhindert, dass der Bereich für <legend>
angepasst wird, da nur die untergeordneten Elemente <div>
ausgerichtet werden.
Filterlabel und Kästchen
Da es sich um ein direkt untergeordnetes Element eines <fieldset>
handelt und die maximale Breite des 30ch
des Formulars nicht überschritten wird, wird der Labeltext möglicherweise umgebrochen, wenn er zu lang ist. Textumbruch ist in Ordnung, aber eine Fehlausrichtung zwischen Text und Kästchen ist nicht gut. Flexbox eignet sich dafür hervorragend.
fieldset > div {
display: flex;
gap: 2ch;
align-items: baseline;
}
Das animierte Raster
Die Layoutanimation wird von Isotope erstellt. Ein leistungsstarkes Plug-in für die interaktive Sortierung und Filterung.
JavaScript
JavaScript wird nicht nur verwendet, um ein ansprechendes animiertes, interaktives Raster zu erstellen, sondern auch, um einige Ecken und Kanten zu glätten.
Normalisierung der Nutzereingabe
Dieses Design hat ein Formular mit zwei verschiedenen Eingabemöglichkeiten, die nicht serialisiert werden. Mit etwas JavaScript können wir die Daten jedoch normalisieren.
Ich habe die Datenstruktur des <select>
-Elements an die Struktur der gruppierten Kästchen angepasst. Dazu wird dem <select>
-Element ein input
-Ereignis-Listener hinzugefügt, wodurch selectedOptions
zugeordnet werden.
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 absenden oder in diesem Fall Isotope anweisen, nach welchen Kriterien gefiltert werden soll.
Statusrollenelement fertigstellen
Das Element zählt und anzeigt nur die Filteranzahl basierend auf der Kästcheninteraktion. Ich fand es jedoch sinnvoll, zusätzlich die Anzahl der Ergebnisse anzugeben und dafür zu sorgen, dass auch die Auswahlmöglichkeiten des <select>
-Elements gezählt werden.
<select>
-Elementauswahl, die sich im counter()
widerspiegelt
Im Abschnitt zur Datennormalisierung wurde bereits ein Listener für die Eingabe 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 Element „state role“ übergeben werden.
let statusRoleElement = document.querySelector('#applied-filters')
statusRoleElement.style.counterSet = selectData.length
Ergebnisse, die im Element role="status"
berücksichtigt werden
:checked
bietet eine integrierte Möglichkeit, die Anzahl der ausgewählten Filter an das Status-Rollenelement weiterzugeben. Die gefilterte Anzahl der Ergebnisse ist jedoch nicht sichtbar.
JavaScript kann auf Interaktionen mit den Kästchen achten und nach dem Filtern des Rasters textContent
hinzufügen, ähnlich wie das <select>
-Element.
document
.querySelector('aside form')
.addEventListener('input', e => {
// isotope demo code
let filterResults = IsotopeGrid.getFilteredItemElements().length
document.querySelector('#applied-filters').textContent = `giving ${filterResults} results`
})
Zusammengenommen vervollständigt diese Arbeit die Ankündigung „2 Filter mit 25 Ergebnissen“.
Jetzt können alle Nutzer unsere hervorragenden Hilfstechnologien nutzen, unabhängig davon, wie sie damit interagieren.
Fazit
Wie würden Sie das machen?
Lassen Sie uns unsere Ansätze diversifizieren und alle Möglichkeiten kennenlernen, wie Sie im Web entwickeln können. Erstelle eine Demo, tweete mir Links und ich füge sie unten in den Abschnitt „Community-Remixe“ hinzu.
Remixe der Community
Noch keine Aktivität hierzu.