Komponente mit Mehrfachauswahl erstellen

Ein grundlegender Überblick darüber, wie Sie eine responsive, adaptive und barrierefreie Komponente mit Mehrfachauswahl erstellen können, um Nutzerfreundlichkeit zu sortieren und zu filtern.

In diesem Beitrag möchte ich meine Gedanken zu einer Möglichkeit zur Erstellung einer Mehrfachauswahlkomponente teilen. Demo ansehen

Demo

Falls Sie Videos bevorzugen, finden Sie 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 überladen.

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.

Vergleichsscreenshot, der die Desktopversion in hellem und dunklem Modus mit einer Seitenleiste mit Kästchen und die mobilen Versionen für iOS und Android mit einem Element für die Mehrfachauswahl zeigt

Die Entscheidung, die integrierte Mehrfachauswahl für Touchbedienung und nicht für Computer zu verwenden, spart Arbeit und schafft Arbeit, bietet aber meiner Meinung nach eine angemessene Benutzererfahrung mit weniger Code-Schulden als die Erstellung der gesamten responsiven Benutzeroberfläche in einer Komponente.

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.

Screenshot-Vorschau des Elements für die Mehrfachauswahl in Chrome auf Android-Geräten, iPhones und iPads Auf dem iPad und dem iPhone ist die Mehrfachauswahl aktiviert. Die Benutzeroberfläche ist für die jeweilige Bildschirmgröße optimiert.

Tastatur und Gamepad

Unten sehen Sie eine Demonstration, wie Sie <select multiple> über die Tastatur verwenden.

Diese integrierte Mehrfachauswahl kann nicht gestaltet werden und wird nur in einem kompakten Layout angeboten, das nicht für die Darstellung vieler Optionen geeignet ist. Sehen Sie nur, dass Sie die Bandbreite der Optionen in diesem winzigen Feld nicht sehen? 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 ein <fieldset>-Element eingeschlossen und mit einem <legend> versehen werden. Wenn HTML auf diese Weise strukturiert ist, erkennen Screenreader und FormData automatisch die Beziehung der Elemente.

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

Screenshot mit einem informativen Overlay für die Legenden- und Feldset-Elemente, auf dem die Farbe und der Elementname zu sehen sind

<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. Es ist, als würden Sie die Interaktion von einer Radio- zu einer Kontrollkästchen-Liste ändern.

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

Screenshot des Desktop-Renderings eines Elements mit Mehrfachauswahl.

Eingaben mit Zählern erfassen, um assistive Technologien 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>

Dieses Element liest Änderungen am Inhalt vor. 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;
}

Die Anzahl ist standardmäßig 0, was sehr gut ist. Standardmäßig ist in diesem Design nichts :checked.

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, können Sie mit der Funktion counter() auf den Wert aus dem Inhalt des Pseudoelements zugreifen:

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

Im HTML-Code für das Statusrollenelement wird jetzt einem Screenreader „2 Filter“ mitgeteilt. 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.

Screenshot des MacOS-Screenreaders mit der Anzahl der aktiven Filter

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

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

Screenshot der Standardstile für ein Feldsatz und eine Legende

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.

Screenshot, der den Abstand zwischen den Eingaben, aber nicht die Legende zeigt

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 in Ordnung. Flexbox eignet sich dafür hervorragend.

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

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.

Screenshot der JavaScript-Konsole der Entwicklertools mit den Ergebnissen für das Zielvorhaben und normalisierte Daten

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 wird in counter() widergespiegelt

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 Rollenelement des Bundesstaats ü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 die Interaktion mit den Kästchen überwachen und nach dem Filtern des Rasters textContent wie das <select>-Element hinzufügen.

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

Screenshot des MacOS-Screenreaders, der Ergebnisse ansagt

Jetzt können alle Nutzer unsere hervorragenden Hilfstechnologien nutzen, unabhängig davon, wie sie damit interagieren.

Fazit

Wie würden Sie es 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.