In-Page Filtered Search With Vanilla JavaScript

Avatar of Hilman Ramadhan
Hilman Ramadhan on

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

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?