Wenn Sie eine Seite mit vielen Informationen haben, ist es eine gute Idee, den Benutzern die Möglichkeit zu geben, nach dem zu suchen, was sie vielleicht suchen. Ich spreche nicht vom Durchsuchen einer Datenbank oder gar vom Durchsuchen von JSON-Daten – ich spreche davon, Text auf einer einzeln gerenderten Webseite zu durchsuchen. Benutzer können bereits die integrierte Browsersuche dafür nutzen, aber wir können dies verbessern, indem wir unsere eigene Suchfunktion anbieten, die die Seite filtert, sodass übereinstimmende Ergebnisse leichter zu finden und zu lesen sind.
Hier ist eine Live-Demo dessen, was wir bauen werden
Ich verwende dieselbe Technik in meinem echten Projekt: https://freestuff.dev/.
Lernen Sie JavaScript kennen!
Nun, Sie kennen JavaScript vielleicht schon. JavaScript wird die gesamte Interaktivität auf dieser Reise übernehmen. Es wird...
- alle Inhalte finden, die wir durchsuchen wollen,
- beobachten, was ein Benutzer in das Suchfeld eingibt,
- den `innerText` der durchsuchbaren Elemente filtern,
- testen, ob der Text den Suchbegriff enthält ( `includes()` ist hier der schwere Heber!), und
- die Sichtbarkeit der (Eltern-)Elemente umschalten, je nachdem, ob sie den Suchbegriff enthalten oder nicht.
Alles klar, wir haben unsere Anforderungen! Lassen Sie uns beginnen.
Die grundlegende Markup
Nehmen wir an, wir haben eine FAQ-Seite. Jede Frage ist eine "Karte" mit einem Titel und einem Inhalt
<h1>FAQ Section</h1>
<div class="cards">
<h3>Who are we</h3>
<p>It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized </p>
</div>
<div class="cards">
<h3>What we do</h3>
<p>It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized </p>
</div>
<div class="cards">
<h3>Why work here</h3>
<p>It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized</p>
</div>
<div class="cards">
<h3>Learn more</h3>
<p>Want to learn more about us?</p>
</div>
Stellen Sie sich vor, es gibt viele Fragen auf dieser Seite.
Um uns auf die Interaktivität vorzubereiten, werden wir diese eine CSS-Zeile haben. Dies gibt uns eine Klasse, die wir je nach Suchsituation hinzufügen oder entfernen können, wenn wir zum JavaScript kommen.
.is-hidden { display: none; }
Fügen wir ein Suchfeld mit einem Ereignis hinzu, das ausgelöst wird, wenn damit interagiert wird.
<label for="searchbox">Search</label>
<input
type="search"
oninput="liveSearch()"
id="searchbox"
>
Die JavaScript-Basislinie
Und hier ist das JavaScript, das alles andere tut!
function liveSearch() {
// Locate the card elements
let cards = document.querySelectorAll('.cards')
// Locate the search input
let search_query = document.getElementById("searchbox").value;
// Loop through the cards
for (var i = 0; i < cards.length; i++) {
// If the text is within the card...
if(cards[i].innerText.toLowerCase()
// ...and the text matches the search query...
.includes(search_query.toLowerCase())) {
// ...remove the `.is-hidden` class.
cards[i].classList.remove("is-hidden");
} else {
// Otherwise, add the class.
cards[i].classList.add("is-hidden");
}
}
}
Sie können wahrscheinlich Zeile für Zeile durchgehen und verstehen, was es tut. Es findet alle Karten und das Eingabefeld und speichert Referenzen darauf. Wenn ein Suchereignis ausgelöst wird, durchläuft es alle Karten und ermittelt, ob der Text in der Karte enthalten ist oder nicht. Wenn der Text in der Karte mit der Suchanfrage übereinstimmt, wird die Klasse `.is-hidden` entfernt, um die Karte anzuzeigen; wenn nicht, ist die Klasse vorhanden und die Karte bleibt verborgen.
Hier ist erneut der Link zur Demo.
Hinzufügen einer Verzögerung
Um sicherzustellen, dass unser JavaScript nicht *zu viel* ausgeführt wird (was die Seite verlangsamen würde), werden wir unsere `liveSearch`-Funktion erst nach einer Wartezeit von "X" Sekunden ausführen.
<!-- Delete on Input event on this input -->
<label for="searchbox">Search</label>
<input type="search" id="searchbox">
// A little delay
let typingTimer;
let typeInterval = 500; // Half a second
let searchInput = document.getElementById('searchbox');
searchInput.addEventListener('keyup', () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(liveSearch, typeInterval);
});
Was ist mit unscharfen Suchen?
Nehmen wir an, Sie möchten nach Text suchen, der für den Benutzer *nicht* sichtbar ist. Die Idee ähnelt einer unscharfen Suche, bei der verwandte Schlüsselwörter das gleiche Ergebnis wie eine exakte Übereinstimmung liefern. Dies hilft, die Anzahl der Karten zu erhöhen, die mit einer Suchanfrage "übereinstimmen" könnten.
Dafür gibt es zwei Möglichkeiten. Die erste ist die Verwendung eines versteckten Elements, wie z.B. eines `span`, das Schlüsselwörter enthält.
<div class="cards">
<h3>Who are we</h3>
<p>It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized</p>
<!-- Put any keywords here -->
<span class="is-hidden">secret</span>
</div>
Dann müssen wir unsere `liveSearch`-Funktion aktualisieren. Anstatt `innerText` zu verwenden, werden wir `textContent` verwenden, um versteckte Elemente einzuschließen. Mehr Details zum Unterschied zwischen innerText und textContent finden Sie hier.
for (var i = 0; i < cards.length; i++) {
if(cards[i].textContent.toLowerCase()
.includes(search_query.toLowerCase())) {
cards[i].classList.remove("is-hidden");
} else {
cards[i].classList.add("is-hidden");
}
}
Versuchen Sie, "secret" in ein Suchfeld einzugeben, es sollte diese Karte anzeigen, obwohl "secret" nicht auf der Seite angezeigt wird.
Ein zweiter Ansatz ist die Suche durch ein Attribut. Nehmen wir an, wir haben eine Galerie von Bildern. Wir können die Schlüsselwörter direkt in das `alt`-Attribut des Bildes einfügen. Versuchen Sie, "kitten" oder "human" in der nächsten Demo einzugeben. Diese Abfragen stimmen mit dem überein, was im `alt`-Text des Bildes enthalten ist.
Damit dies funktioniert, müssen wir `innerText` in `getAttribute('alt')` ändern, da wir neben dem, was tatsächlich auf der Seite sichtbar ist, auch die `alt`-Attribute durchsuchen möchten.
for (var i = 0; i < cards.length; i++) {
if(cards[i].getAttribute('alt').toLowerCase()
.includes(search_query.toLowerCase())) {
cards[i].classList.remove("is-hidden");
} else {
cards[i].classList.add("is-hidden");
}
}
Je nach Ihren Bedürfnissen könnten Sie Ihre Schlüsselwörter in ein anderes Attribut einfügen, oder vielleicht ein benutzerdefiniertes.
Vorbehalt
Auch hier handelt es sich nicht um eine Suchtechnologie, die durch Abfrage einer Datenbank oder einer anderen Datenquelle funktioniert. Sie funktioniert nur, wenn Sie alle durchsuchbaren Inhalte auf der Seite im DOM haben, bereits gerendert.
Also ja, das ist zu beachten.
Zusammenfassung
Offensichtlich mag ich diese Technik sehr, genug, um sie auf einer Produktionsseite zu verwenden. Aber wie könnte man etwas wie das noch nutzen? Eine FAQ-Seite ist ein klarer Kandidat, wie wir gesehen haben, aber jede Situation, die das Filtern von Inhalten jeglicher Art erfordert, eignet sich für diese Art von Dingen. Selbst eine Bildergalerie könnte funktionieren, indem der Trick mit dem versteckten Eingabefeld verwendet wird, um den Inhalt der `alt`-Tags der Bilder zu durchsuchen.
In jedem Fall hoffe ich, dass Sie dies hilfreich finden. Ich war überrascht, dass wir mit ein paar Zeilen Vanilla JavaScript eine ziemlich robuste Suchlösung erhalten können.
Haben Sie diese Technik schon einmal verwendet oder etwas Ähnliches? Was war Ihr Anwendungsfall?
Ich verwende oft eine Tabellenfilterung und eine Listenfilterung, wenn das Abrufen aller Ergebnisse schneller ist als die Paginierung, da die Filterung schneller ist als die Suche. Die von Ihnen verwendete funktioniert für meinen Fall nicht, da IE in meiner Welt immer noch existiert.
https://gist.github.com/miwebguy/417160798c8c1b275a0d15d04bf01954
Danke Bill fürs Teilen
Danke für das coole Tutorial! Ich verwende selbst oft ähnliche Techniken, aber sie sind möglicherweise nicht vollständig barrierefrei oder entsprechen zumindest nicht vollständig den Barrierefreiheitsstandards.
Formulare ohne Submit-Button (z.B. Filterformulare, die sofort filtern, wenn Sie tippen oder ein Auswahlelement verwenden) sind problematisch. Ich empfehle diesen Link als Ausgangspunkt für weitere Recherchen: https://stackoverflow.com/questions/42811178/is-it-possible-to-make-a-accessible-form-without-a-submit-button
Ich selbst baue immer noch Filter ohne Submit-Buttons, besonders wenn die Daten bereits geladen sind und der Submit-Button die Dinge nur mit einem unnötigen zusätzlichen Klick verlangsamen würde. Manche Benutzer empfinden das vielleicht als störend oder schwieriger zu bedienen, aber es sollte den Filter nicht unzugänglich machen. Um Dinge zugänglicher zu machen, füge ich eine Statusmeldung mit role="status" hinzu.
Sie können hier über das Attribut lesen: https://www.w3.org/WAI/WCAG21/Techniques/aria/ARIA22
Wenn jemand mehr darüber weiß, wie (oder ob) Filter ohne Submit-Button barrierefrei gemacht werden können, wäre das sehr willkommen!
Danke Jonathan für den Hinweis
Jonathans Kommentar verdient Sterne. Sehr hilfreich.
Es mag kitschig klingen, aber die Einbeziehung eines Suchbuttons ist für die Leute weniger verwirrend, wenn sie erwarten, auf etwas zu klicken. Die Einbeziehung eines Suchbuttons könnte also eine gute Sache sein, auch wenn die Suche "live" ist.
Natürlich gibt es viel Raum für Optimierung: Zum Beispiel, indem man eine Liste von Elementen beibehält, anstatt sie jedes Mal neu abzufragen. Ich wollte auch sagen "nur die sichtbaren filtern, wenn beim keyup der Eingabewert nicht länger ist", aber das würde beim Kopieren/Einfügen nicht funktionieren.
Gut! Könnte besser sein, wenn Sie JSON für die Suche und Generierung des Inhalts verwenden würden. Dann müssten Sie keine Schlüsselwörter in HTML verstecken.
Die fuse-Bibliothek ist eine großartige unscharfe Suche, die Sie verwenden können.
Versehentlich habe ich ein ähnliches, aber viel kleineres (825b) npm-Paket für die Suche und Filterung von JSON-Arrays veröffentlicht.
Sie hätten bessere semantische HTML mit einer Definitionsliste (DL DT und DD Elemente) verwenden können.
Auch die Verwendung von Datenattributen (data-*) kann relevanter sein, um Daten zu HTML-Elementen hinzuzufügen, die mit JavaScript manipuliert werden sollen.
So ein tolles Tutorial.. Mas Hilman, super!!!
Alles in Ordnung. Aber…
Versuchen Sie, im polnischen Sprachgebrauch 'Śreniawa' zu suchen, und nichts funktioniert richtig... :-(
Es funktioniert nicht, wenn Sie auf 'x' klicken, um die Suche zu löschen.
Außerdem ist es ineffizient, Sie wandeln die Suche für jedes filterbare Element in Kleinbuchstaben um.
Wenn Ihre Liste lang wird, hat diese Methode Leistungseinbußen.
Eine großartige Lösung, die ich gesehen habe, ist, was Jets.js gemacht hat, wo sie eine einzelne CSS-Regel manipulieren, um die übereinstimmende Suchregel zu verstecken oder anzuzeigen.
Wenn Sie also nach "bob" suchen, schreibt die Engine dynamisch so etwas wie
und lässt die CSS-Engine die visuelle Filterung übernehmen.
Vanilla JS-Lösung für längere Listen. Meine hatte 1700 Elemente.
let timer;
const input = document.querySelector("#searchbox");
input.addEventListener("keyup", function (liveSearch) {
clearTimeout(timer);
timer = setTimeout(() => {
const items = document.querySelectorAll(".link_button");
for (let item of items) {
item.style.display = item.textContent.includes(liveSearch.target.value)
? "inline-block"
: "none";
}
}, 1000);
});
Gibt es eine Möglichkeit, so etwas zu tun, aber die Funktion durchsucht mehrere Seiten? Nur mit JavaScript?
Hallo! Wie füge ich einen Button hinzu, damit die Ergebnisse angezeigt werden? TX
Fantastisches Tutorial, habe es verwendet, um Suchbegriffe in Tabellen mit Tausenden von Zeilen bereitzustellen! Daten sind statisch, also kein Problem!
Beim Ausführen mit React bin ich auf ein Problem gestoßen…
Das Element wurde gerendert, nachdem der Event-Listener hinzugefügt wurde, also habe ich einen try/catch-Block hinzugefügt, um das Problem zu lösen.
Nochmals vielen Dank!
Ich verstehe nicht, warum Bulma eine Voraussetzung dafür ist. Wenn ich Bulma entferne, ist es eine Voraussetzung dafür? Das ist eine CSS-Bibliothek und wenn ich sie entferne, funktioniert das JS nicht mehr, aber Sie erwähnen das nicht in Ihrem Artikel.
Gibt es eine Möglichkeit, dies zu animieren, damit die Übergänge etwas schöner aussehen?
Wie füge ich Kategorien zum Filtern hinzu? :-(
Ich nehme an, das ist eine grundlegende Frage, aber JS ist neu für mich. Ich habe Livesearch verwendet und habe 212 Karten, die ein Mitgliedsverzeichnis bilden, mit dem Seitenframework BS 4.6 und PHP, und wenn ich den Filter durchsuche, ist er sofort, aber ich möchte die Anzahl der gefilterten Karten anzeigen, also den JS-Gesamtbetrag an eine PHP-Variable übergeben. Könnten Sie einen Hinweis geben, wie ich das erreichen kann. Großartiges Skript aber
Das funktioniert für mich wirklich gut. Danke
Könnte es modifiziert werden, um auch Regex-Suchen zuzulassen?