Formulare/Suchen und Filtern

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche
Es wird immer wieder gefragt, wie man eine Suchfunktion in einem HTML-Dokument oder auf einer Webseite realisieren kann.

Dieses Tutorial soll einige Wege aufzeigen und dabei den Unterschied zwischen dem Durchsuchen von Inhalten einerseits und dem Filtern von Suchvorschlägen andererseits herausstellen.

Vorüberlegungen

Eine Suche innerhalb der aktuellen Seite ist in jedem Browser über das Menü oder mit STRG F möglich. Diese Tastenkombination ist vielen Nutzern bereits aus Textverarbeitungen wie MS Word bekannt[1] und Sie sollten unbedingt darauf verzichten, diese Funktion nachzuprogrammieren.

Eine Suche innerhalb einer Website mit mehreren Unterseiten ist entweder bereits in vielen CMS integriert oder muss serverseitig erfolgen. Alternativ kann man auf externe Suchmaschinen zurückgreifen, was Nutzer aber aus dem eigenen Webauftritt wegführt.

Durchsuchen einer bestimmten Website mit duckduckgo
<form role="search" method="GET" action="https://duckduckgo.com/">
	<input name="q" type="search" aria-label="Suche auf selfhtml.org"/>
	<input name="site" type="hidden" value="selfhtml.org"/>
	<button>Suchen</button>
</form>

Duckduckgo erwartet für seine Suchfunktion bestimmte URL Parameter. Das Suchformular erreicht das, indem die Methode GET eingestellt wird und die Eingabefelder passend benannt werden.

q
Der Begriff oder die Begriffe, wonach Duckduckgo suchen soll. Dies ist ein offenes Eingabefeld mit type="search".
site
Der Name der Domain, innerhalb der gesucht werden soll. Für eine site-spezifische Suche soll dieser Name nicht vom Benutzer eingegeben werden und muss auch nicht sichtbar sein, darum ist dieses Feld als type="hidden" angegeben.

Externe Suchmaschinen können natürlich immer nur das indexieren und durchsuchen, was auf Ihren HTML Seiten öffentlich zugänglich ist. Wenn Sie einen login-geschützten Bereich haben, sind Sie darauf angewiesen, eine eigene Suche zu programmieren. Das geschieht dann aber nicht mit JavaScript im Browser, sondern auf Ihrem Server und ist nicht Gegenstand dieses Artikels.

gefilterte Suche mit JavaScript

Auf umfangreichen Seiten mit vielen Informationen ist es eine gute Idee, den Benutzern eine Möglichkeit zu geben, die Informationsmenge an Hand von bestimmten Kriterien zu reduzieren oder zumindest die gewünschten Funktionen deutlich hervorzuheben.

Die im Browser integrierte Suchfunktion kann nach Wörtern suchen und diese hervorheben. Eine Suche dieser Art müssen wir deshalb nicht selbst bauen. Es ist auch ziemlich komplex, in einem mit HTML gespickten Text einen Suchbegriff hervorzuheben, wenn der Text dieses Begriffs durch HTML Markup unterbrochen ist. Für den Selbstbau geeignet ist dagegen ist eine Suchfunktion, die auf der Ebene von HTML Elementen relevante Informationseinheiten hervorhebt, oder weniger relevante Bereiche ausblendet.[2]

Das Script wird …

  • beobachten, was ein Benutzer in die Sucheingabe eingibt,
  • den innerText der durchsuchbaren Elemente filtern,
  • testen, ob der Text den Suchbegriff enthält (.includes() ist hier das A und O!), und
  • die Sichtbarkeit der (übergeordneten) Elemente umzuschalten, je nachdem, ob sie den Suchbegriff enthalten oder nicht

Suchformular

Der Inhalt unserer Webseite besteht aus mehreren article-Elementen, die die Adresse und ein Beispielfoto enthalten:

Vogelparks In Deutschland ansehen …
<article id="de10319">
	<h2>Tierpark Berlin</h2>
	<p>
		<img src="Sagittarius_serpentarius.jpg" alt="Sekretärvogel im Tierpark Belrin">
Am Tierpark 125
10319 Berlin</p>
</article>

<article id="de10787">
<h2>Zoologischer Garten Berlin AG</h2>
<p>
<img src="Glaucidium_perlatum_Berlin_Zoo.jpg" alt="Eule im Zoologischen Garten in Berlin">
Hardenbergplatz 8
10787 Berlin

<br>
<img src="Flamingo_Winter_Zoologischer_Garten_Berlin_cropped.jpg" alt="Flamingos im Winter"></p>
</article>
...

Eine in der Seite integrierte Filterfunktion benötigt JavaScript und kann daher nur ein progressive enhancement sein. Die Benutzeroberfläche für die Filterung sollte daher standardmäßig ausgeblendet sein und nur sichtbar werden, wenn JavaScript ausgeführt wird. Ein <form>-Element ist nicht nötig, aber ein <search>-Element bietet sich als Container für das Eingabefeld an.

Eingabe des Suchbegriffs ansehen …
    <search id="filter">
        <label for="suchbegriff">Zeige nur Ergebnisse mit </label>
        <input type="search" id="suchbegriff">
    </search>

Das Suchformular besteht aus einem Input type="search"-Feld, dass durch ein label-Element passend beschriftet ist.

Javascript

die Basis-Version

Die eigentliche Suche wird von der Funktion liveSearch() übernommen:

Suchscript ansehen …
function liveSearch() { 
  let main = document.querySelector("main");
  let alle_artikel = main.querySelectorAll('article');
  let filterbegriff = document.querySelector('#filter input').value.toLocaleLowerCase();
  if (filterbegriff.trim() == "") {
    main.classList.remove("is-filtered");
  }
  else {
    main.classList.add("is-filtered");
    // Die Artikel auf der Seite durchlaufen. 
    for (let artikel of alle_artikel) {
      // Wenn der Suchbegriff als (sichtbarer) Text auftaucht, den Artikel als Treffer markieren
      let relevant = artikel.innerText.toLocaleLowerCase().includes(filterbegriff);
      artikel.classList.toggle('is-relevant', relevant);
    }
  }
}

Die Funktion ermittelt mit querySelectorAll alle <article>-Elemente im <main>-Bereich. Das Ergebnis steht als eine statische NodeList zur Verfügung. Seitdem man auf den Internet Explorer keine Rücksicht mehr nehmen muss, können NodeList-Objekte mit einer for...of-Schleife einfach durchlaufen werden.

Der Filterbegriff wird aus dem Suchfeld (ein input-Element im einem Container mit id="filter") ausgelesen und in Kleinschrift umgewandelt. Dadurch wird eine von Groß-/Kleinschreibung unabhängige Suche ermöglicht.

Vor der eigentlichen Filterung wird geprüft, ob eine Filterbedingung eingegeben wurde. Wenn nicht, wird lediglich die is-filtered Klasse vom main-Element entfernt. Es bleibt dem CSS überlassen, daraus die ungefilterte Darstellung zu erzeugen.

Ist eine Filterbedingung vorhanden, wird im main-Element die is-filtered Klasse gesetzt und die Artikel durchsucht. Diejenigen Artikel, in deren innerText der Suchbegriff gefunden wird, erhalten die is-relevant Klasse. Von den übrigen wird sie entfernt. Die toggle Methode der classList ermöglicht mit ihrem zweiten Parameter ein gezieltes Setzen oder Entfernen einer Klasse.

eine übereifrige Suche bremsen

Damit das Suchscript nicht bereits nach dem ersten eingegebenen Zeichen sucht, bauen wir einen einfachen Debouncer ein. Dazu nutzen wir den SetTimeout-Timer und die Möglichkeit, einen gestarteten Timer mit ClearTimeout wieder zu entfernen.

Verzögerung, bevor liveSearch greift ansehen …
let typingTimer;
let typeInterval = 500;
let searchInput = document.querySelector('#search');

searchInput.addEventListener('input', () => {
    clearTimeout(typingTimer);
    typingTimer = setTimeout(liveSearch, typeInterval);

Mit setTimeout() wird die Funktion liveSearch() erst nach einem bestimmten Zeitraum aufgerufen, den Sie in der Variable typeInterval festlegen können. Tippt der Anwender ein weiteres Zeichen, bevor die Suche begonnen wurde, so wird der bestehende Timer wieder gelöscht.

Alternativtexte berücksichtigen

In der Webseite enthaltene Bilder müssen einen Alternativtext beinhalten, der nicht nur für Screenreader, sondern eben auch für Suchmaschinen und Suchscripte genutzt werden kann. Das soll nun noch ergänzt werden:

<article id="de29664">
<h2>Vogelpark Walsrode</h2>
<p>
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/24/Vogelpark_Walsrode_2012_%28140%29.jpg/640px-Vogelpark_Walsrode_2012_%28140%29.jpg" alt="Tukan im Vogelpark Walsrode">
Am Vogelpark
29664 Walsrode 
<span hidden>Tipp</span>
</p>
</article>

Um neben dem Textinhalt der Textauszeichnungselemente auch den eigentlich „unsichtbaren“ Alternativtext von Bildern zu durchsuchen, muss dieser ebenfalls erfasst werden. Die Suchfunktion wird damit deutlich umfangreicher, deshalb ist die eigentliche Relevanzermittlung für einen Artikel in eine weitere Funktion ausgelagert worden. Das Codebeispiel ist im Übrigen auf den relevanten Teil reduziert worden, den vollständigen Code finden Sie mit "ausprobieren" im Frickl.

Auch in alt-Texten suchen ansehen …
function liveSearch() {
  // ...
  let alle_artikel = main.querySelectorAll('article');
  let filterbegriff = document.querySelector('#filter input').value.toLocaleLowerCase();
  // ...
  for (let artikel of alle_artikel) {
    artikel.classList.toggle('is-relevant', isArticleRelevant(artikel, filterbegriff));
  }
}

// Durchsuchen eines Artikels auf den Filterbegriff
function isArticleRelevant(artikel, filterbegriff) {
  // Wenn der Suchbegriff als (sichtbarer) Text auftaucht, den Artikel als Treffer markieren
  if (artikel.innerText.toLocaleLowerCase().includes(filterbegriff)) {
    return true;
  }
  // Wenn nicht, auch die alt-Texte der Bilder durchsuchen
  let bilder = artikel.querySelectorAll("img");
  for (let bild of bilder) {
    if (bild.alt.toLocalLowerCase().includes(filterbegriff)) {
       return true;
    }
  }
  // Nichts gefunden, nicht relevant
  return false;
}

Ergebnisanzeige

Da sich die Webseite nach jeder Suche ändert, dies z.B. beim Suchbegriff „Berlin“ aber nicht gleich offensichtlich ist, kann es empfehlenswert sein, in einer Status Message die Anzahl der Suchtreffer anzuzeigen [4]

<div role="status">5 Ergebnisse </div>

Combo Box

Eine Combobox ist ein Widget, das aus der Kombination von zwei verschiedenen Elementen besteht:[5]

  1. ein einzeiliges Textfeld und
  2. ein zugehöriges Popup-Element, das dem Benutzer hilft, den Wert des Textfeldes einzustellen. Das Popup-Element kann eine Listbox, ein Grid, ein Baum oder ein Dialog sein. Viele Implementierungen enthalten auch ein drittes optionales Element - eine grafische Schaltfläche neben dem Textfeld, die die Verfügbarkeit des Popup-Fensters anzeigt. Wenn die Schaltfläche aktiviert wird, wird das Popup angezeigt, wenn Vorschläge verfügbar sind.[6]

Im Forum wurde gefragt, wie man ein oben vorgestelltes Suchfeld mit einer Umkreissuche und einer Auto-Vervollständigung von Adressen kombiniert. Hier gibt es mehrere serverseitige Alternativen, die den Umfang dieses Tutorials aber sprengen würden.[7][8]

Suchformulare mit CSS gestalten

Auch eine noch so ausgeklügelte Navigation und ein übersichtlicher Seitenaufbau können verhindern, dass Benutzer Ihrer Webseite gewünschte Inhalte nicht auf Anhieb finden. Hier ist ein schnell auffindbares Suchformular nötig, damit vorhandene Inhalte auch genutzt werden können.

In Formulare/Was ist ein Webformular? erfahren Sie, was beim Absenden eines Suchformular passiert. In diesem Kapitel lernen Sie, wie Sie Suchformulare bedienungsfreundlicher gestalten, damit Nutzer Ihrer Webseite gewünschte Inhalte besser finden.


animierte Suchfelder

Häufig soll ein Suchfeld in ein bestehendes Layout eingepasst werden und die Breite etwa eines Navigationslinks oder einer Sidebar einnehmen. Andererseits sollen bei einer Suche auch lange Eingaben vollständig zu sehen sein. Dies können Sie erreichen, indem Sie das Suchfeld mit CSS anders gestalten, wenn es durch eine Eingabe den focus erhält.

Beispiel ansehen …
<label for="suche">Suchbegriff eingeben</label> 
<input type="search" id="suche" placeholder="Suche...">

Das Eingabefeld vom type="search" erhält mit dem placeholder-Attribut einen sichtbaren Hinweis. Daneben wird mit dem label-Element eine Beschriftung hinzugefügt, die mit einem for-Attribut mit dem Eingabefeld verbunden wird.

Sie könnten auf das for-Attribut verzichten, wenn Sie das Eingabefeld innerhalb des label notieren. Allerdings ist die hier verwendete Reihenfolge für das spätere Gestalten des label wichtig.


Beispiel ansehen …
#suche {
  border: 2px solid #999;
  border-radius: 0.5em;;
  font-size: 1.2em;
  width: 10em;
  transition: width 0.5s ease-in-out;
}
 
#suche:focus {
  font-size: 1.5em;
  width: 50%;
}

Das Eingabefeld erhält einen dickeren grauen, abgerundeten Rand. Die Schriftgröße wird erhöht. Die Breite ist abhängig der Schriftgröße 10em breit.

Sobald das Eingabefeld durch die Maus oder Tastatur angesteuert wird, erhält es den Fokus. Dies kann in CSS als Pseudoklasse mit anderen Formatfestlegungen definiert werden. So erhält das Suchfeld eine größere Schriftgröße und eine Breite von 50%. Durch die transition-Eigenschaft wird ein weicher Übergang von einer halben Sekunde erreicht.


Suchsymbol

Eine Lupe gilt als allgemein verständliches Symbol für ein Suchformular. Eine Einbindung einer externen Rastergrafik mit background-image würde aber einen zusätzlichen HTTP-Request benötigen.

Eleganter wäre die Darstellung eines Unicode-Zeichens, das stets passend skaliert. Es gibt die Unicode-Symbole U+1F50D LEFT-POINTING MAGNIFYING GLASS (🔍) und U+1F50E RIGHT-POINTING MAGNIFYING GLASS (🔎), die aber nicht in allen Schriftarten umgesetzt sind. Darüber hinaus darf input als replaced element keinen, auch keinen generierten Inhalt erhalten. Dies ist wichtig, da ein Unicode-Zeichen nicht als Hintergrundbild, sondern als (Text)Inhalt eines Pseudoelements eingebunden würde.

Aus diesem Grund wird im vorliegenden Beispiel eine base64-codierte SVG-Grafik in das CSS eingebunden.

Beispiel ansehen …
#suche {
  width: 10em;
  height: 60px;
  border: 2px solid #999;
  border-radius: 0.5em;
  font-size: 1.2em;
  transition: width 0.5s ease-in-out;
background:url('data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAASAAD
...
/pcwn9yppSVGRajT8fky+QWgk0+qdcu77uD5lZAAAAAAAA6Ux3AAEV6zGTp3RS5tlfaufCgBQf/9k=') no-repeat right;
}

#suche:focus {
  font-size: 1.5em;
  width: 50%;
}
Die SVG-Grafik sorgt für einen 3D-Look. Sie wird aber nicht referenziert, sondern als base64-codierte Grafik eingebunden, sodass kein weiterer HTTP-Request nötig ist.

minimalistische Variante

Beispiel ansehen …
#suche {
  width: 10em;
  border: none;
  border-bottom: 2px solid #999;
  font-size: 1.2em;
  transition: width 0.5s ease-in-out;
}
 
#suche:focus {
  font-size: 1.5em;
  width: 50%;
}
Das Suchformular ist unsichtbar und wird nur durch die untere Linie gekennzeichnet. Erst ein Klick auf das Schlagwort "Suche" zeigt den Textcursor.

Suchvorschläge mit datalist

Dieses Beispiel beschreibt, wie Sie in einem Eingabefeld die Eingabe durch Vorschläge aus einer Liste unterstützen können. Der Anwender kann diese Vorschläge annehmen, aber auch einen beliebigen anderen Text eingeben.

Im Idealfall werden Sucheingaben durch AjaX automatisch ergänzt. HTML5 kennt als Ergänzung das autocomplete-Attribut, das dann aus bisherigen Eingaben des Benutzers einen Wert errät und vorschlägt.

Darüberhinaus können Sie dem Benutzer mit dem datalist-Element von Ihnen gewählte, häufig verwendete Suchbegriffe vorschlagen.

Beispiel ansehen …
<form action="#">
  <p>
    <label>
      Vogelart
      <input type="search" list="Vögel">
      <datalist id="Vögel">
        <option value="Amsel"> 
        <option value="Buntspecht"> 
        <option value="Drossel"> 
        <option value="Eisvogel"> 
        <option value="Fink"> 
        <option value="Graugans"> 
        <option value="Meise">
        <option value="Spatz"> 
        <option value="Specht"> 
      </datalist> 
    </label>
    <button>finden!</button>
  </p>
</form>

Hier ist ein Eingabefeld vom Typ search zu sehen, welches als Eingabemöglichkeiten verschiedene Vogelnamen vorschlägt. Der Zusammenhang zwischen dem Eingabefeld und der Liste an Vorschlägen wird dadurch hergestellt, dass der Bezeichner Vögel sowohl im list-Attribut des input-Elements, als auch im id-Attribut des datalist-Elements steht.


Weblinks

  1. SELF-Forum: Suchfunktion auf Homepage
  2. css-tricks: In-Page Filtered Search With Vanilla JavaScript von Hilman Ramadhan, Oct 26, 2021
  3. Search Vs. Filter- what is the difference (ux.stackexchange.com)
  4. W3C: Using role=status to present status messages
  5. W3C: role="combobox" WAI-ARIA
  6. W3C: 3.8 Combo Box WAI-ARIA Authoring Practices 1.1
    W3C Working Group Note 14 August 2019
  7. https://wet-boew.github.io/wet-boew-documentation/research/1-datalist-JSON-suggestion.html
  8. HTML Combobox With JavaScript and CSS | Combobox Types & Patterns


Siehe auch