Kennst du das:
Du brauchst auf einer Seite ein paar Filter für Karten oder Listen, baust schnell etwas mit JavaScript zusammen, auf der nächsten Seite wieder, diesmal ein wenig anders, und nach einem Jahr hast du vier Varianten von fast demselben Filter im Projekt liegen.
Genau das hat mich irgendwann so genervt, dass ich mir eine eigene kleine Library gebaut habe: GenericElementFilter. Ich wollte kein großes Framework nur für ein paar Filter, sondern etwas Leichtes, das mit normalem HTML und ein bisschen JavaScript funktioniert.
In diesem Artikel zeige ich dir die Idee dahinter und am Ende auch, wie du die Library konkret einsetzt.
Der Klassiker
if Abfragen auf Klassen oder TexteAm Ende ist alles sehr speziell verdrahtet. Besonders nervig wird es, wenn
Eigentlich ist das Problem total generisch:
Form Elemente liefern einen Filterzustand,
HTML Elemente tragen Metadaten,
sichtbar bleibt nur, was passt.
Also habe ich mir die Frage gestellt: wie kann ich das einmal sauber lösen und dann immer wieder benutzen.
name bei den Filtern, data Attribute auf den ElementenDie Basis von GenericElementFilter ist sehr einfach:
name Attributdata AttributeBeispiel für eine Liste von Veranstaltungen, die du nach Typ und Region filtern willst:
<div class="event-filters">
<label>
Art der Veranstaltung
<select name="type">
<option value="all">Alle</option>
<option value="workshop">Workshop</option>
<option value="talk">Vortrag</option>
<option value="online">Online Format</option>
</select>
</label>
<label>
Region
<select name="region">
<option value="all">Alle Regionen</option>
<option value="north">Nord</option>
<option value="south">Süd</option>
<option value="east">Ost</option>
<option value="west">West</option>
</select>
</label>
</div>
<div class="event-list">
<article class="event-card"
data-type="workshop"
data-region="south">
…
</article>
<article class="event-card"
data-type="talk"
data-region="north">
…
</article>
</div>
Das Mapping ist klar:
name="type" passt zu data-typename="region" passt zu data-regionIn der Library gibt es außerdem das Konzept allValue, standardmäßig ist das der Wert "all". Dieser Wert bedeutet: dieser Filter ist im Moment deaktiviert und wird bei der Berechnung ignoriert. ([GitHub][1])
So entsteht eine einfache Regel:
allValue ist neutralBevor ich zur Library komme, einmal kurz die Punkte, die mir wichtig waren und die GenericElementFilter abdeckt:
allValue lässt sich jeder Filter mit einer Option wieder ausknipsen, ohne Spezialcode.data Attribute. ([GitHub][1])aria live auszeichnen. ([GitHub][1])data Attribut mehrere Werte speichern willst, gibt es einen matchMode "contains", der einzelne Tokens erkennt. Praktisch für Tags oder Schlagwörter. ([ulrischa][2])Ich hätte natürlich auch einfach React, Vue oder ein anderes Framework für solche Filter einsetzen können. Für das konkrete Problem war mir das aber zu schwergewichtig.
Ziele der Library:
data AttributeDaher ist GenericElementFilter eine einzelne Klasse, die du mit einem Konstruktor verwendest oder über eine Hilfsfunktion bindAll, die auf Basis von data Attributen arbeitet. ([GitHub][1])
Für Barrierefreiheit war mir wichtig, dass Nutzerinnen und Nutzer mit Screenreader verstehen, was beim Filtern passiert.
Die Library unterstützt dafür zwei Dinge:
In deinem HTML kann das so aussehen:
<p class="event-filter-status" aria-live="polite"></p>
<p class="event-no-results" hidden>
Es wurden keine Veranstaltungen gefunden, die zu Ihrer Auswahl passen.
</p>
Und im JavaScript konfigurierst du das über statusSelector und noResultsSelector. Außerdem kannst du mit statusFormatter steuern, wie die Meldung genau klingt. ([GitHub][1])
Die Bibliothek kümmert sich darum, dass bei jeder Änderung des Filterzustands die passenden Texte gesetzt werden und das Element für die Meldung ohne Treffer ein oder ausgeblendet wird. Du musst nur noch im Markup aria live setzen, damit Screenreader das mitbekommen.
Ein Filter ist ein klassischer Zustandwechsel: vorher viele Elemente, nachher nur ein Teil. Ohne Animation wirkt das oft wie ein abruptes Wegspringen.
Hier kommt die View Transition API ins Spiel.
In Kurzform:
document.startViewTransition vorhanden ist.viewTransitionPrefix angibst, wird der komplette Filter Vorgang in diese Transition gehüllt. ([GitHub][1])Ein möglicher Konfigurationsausschnitt:
const filter = new GenericElementFilter(".event-card", {
root: document.querySelector(".event-section"),
filtersSelector: ".event-filters [name]",
statusSelector: ".event-filter-status",
noResultsSelector: ".event-no-results",
allValue: "all",
viewTransitionPrefix: "events",
});
In modernen Browsern läuft der Filter dann in einer View Transition. So kannst du zum Beispiel im CSS mit Selektoren arbeiten, die auf diesen Prefix Bezug nehmen, und das Ein und Ausblenden der Karten animieren. In Browsern ohne View Transition API passiert einfach ein normaler Wechsel ohne Animation. Du musst nichts speziell abfangen, das ist automatisch ein progressives Enhancement.
Die Demo Seite zeigt dir diese Mechanik in Aktion, inklusive verschiedener Filtervarianten und Zustände: GenericElementFilter Demo Seite. ([ulrischa][2])
Stellen wir uns vor, du hast eine Seite mit Fortbildungen und willst nach Art der Veranstaltung und Region filtern.
<section class="event-section">
<div class="event-filters">
<label>
Art der Veranstaltung
<select name="type">
<option value="all">Alle</option>
<option value="workshop">Workshop</option>
<option value="talk">Vortrag</option>
<option value="online">Online Format</option>
</select>
</label>
<label>
Region
<select name="region">
<option value="all">Alle Regionen</option>
<option value="north">Nord</option>
<option value="south">Süd</option>
<option value="east">Ost</option>
<option value="west">West</option>
</select>
</label>
</div>
<p class="event-filter-status" aria-live="polite"></p>
<p class="event-no-results" hidden>
Es wurden keine Veranstaltungen gefunden, die zu Ihrer Auswahl passen.
</p>
<div class="event-list">
<article class="event-card"
data-type="workshop"
data-region="south">
<h3>Workshop Klima Kommunikation</h3>
<p>Präsenzveranstaltung, Region Süd.</p>
</article>
<article class="event-card"
data-type="talk"
data-region="north">
<h3>Vortrag Fließgewässer in Zeiten des Klimawandels</h3>
<p>Präsenzveranstaltung, Region Nord.</p>
</article>
<article class="event-card"
data-type="online"
data-region="all">
<h3>Online Reihe Umwelt Daten verständlich erklären</h3>
<p>Online Format, Teilnahme aus allen Regionen möglich.</p>
</article>
</div>
</section>
Die Struktur ist simpel:
namedata AttributeJetzt kommt die Library ins Spiel. Du bindest das Skript ein, zum Beispiel die gebaute Version aus dem Repository, und initialisierst dann den Filter:
<script src="GenericElementFilter.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
const filter = new GenericElementFilter(".event-card", {
root: document.querySelector(".event-section"),
filtersSelector: ".event-filters [name]",
statusSelector: ".event-filter-status",
noResultsSelector: ".event-no-results",
allValue: "all",
statusFormatter: (count, total) => {
if (count === total) {
return `Alle ${total} Veranstaltungen werden angezeigt.`;
}
if (count === 0) {
return "Es werden keine Veranstaltungen angezeigt.";
}
if (count === 1) {
return "Eine Veranstaltung wird angezeigt.";
}
return `${count} Veranstaltungen werden angezeigt.`;
},
viewTransitionPrefix: "events",
});
// Optional: Reset Button anbinden, falls vorhanden
const resetButton = document.querySelector(".event-filters-reset");
if (resetButton) {
resetButton.addEventListener("click", () => filter.reset());
}
});
</script>
Was du damit erledigt hast:
Wenn du eher mit data Attributen arbeiten willst, kannst du dich stark an den Beispielen im README und auf der Demo Seite orientieren. Dort siehst du auch Varianten mit Tabellenzeilen, Tag Filtern und dynamischen Inhalten. ([GitHub][1])
Wenn du tiefer einsteigen willst, schau dir direkt das Repository an:
GitHub GenericElementFilter
Und zum Ausprobieren lohnt sich die Demo:
GenericElementFilter Demo Seite
Als erster einen Kommentar schreiben.
Schreibe einen Kommentar