toscho.design

Suchformular in HTML5

Ein großer Teil der HTML5-Spezifikation sieht aus, als werde bislang ungenormtes Browserverhalten schlicht dokumentiert. Aber der HTML-3.2-Geruch verfliegt, sobald wir unser Auge auf Formulare richten.

Mein Suchformular habe ich endlich ganz und gar auf die neuen Verhältnisse umgestellt.

Beispiel

<form 
  method="GET" 
  id="searchform" 
  accept-charset="utf-8" 
  action="/"
>
  <div>
    <input 
      title="Suche" 
      type="search" 
      value="<?php the_search_query(); ?>" 
      name="s" 
      id="s" 
      pattern=".+" 
      required
    > 
    <input 
      type="submit" 
      id="searchsubmit" 
      value="Suche"
    >
  </div>
</form>

Markup

Na, um das herauszufinden, braucht niemand einen Artikel. Ich möchte aber gerne meine Gründe mit euch diskutieren.

Auf das Element label verzichte ich. Das Eingabefeld hat ein title-Attribut, und der Button gleich danach trägt einen unmißverständlichen Text. Damit kommt auch der Nutzer eines Screenreaders gut zurecht.

Das Attribut accept-charset fungiert als redundante Sicherheitsleine. Ich gebe die Zeichenkodierung im HTTP-Header schon an, und im meta-Element steht sie auch.

Interessant wird endlich der Wert des Attributes type im ersten input-Element: search wurde von Apple durch die Hintertür in HTML5 eingeschmuggelt, sieht jetzt aber doch recht vernünftig aus.

Suchfeld in Safari 3

Suchfeld in Safari 3

Safari hat solche Elemente in der Version 3 noch generell mit abgerundeten Ecken verziert.

Das hat sich aber mit einigen Formatierungen gebissen, die hier und da auftauchten (insbesondere border, outline und background), und inzwischen beschränkt sich zumindest die Windowsversion auf eine Lupe und einen Pfeil.

Suchfeld in Safari 4

Suchfeld in Safari 4

Browser, die diesen Attributwert nicht kennen, unterstellen automatisch type="text".

Wenn jemand schon etwas gesucht hat, dann steht der Suchbegriff natürlich im Feld, damit derjenige seine Anfrage bei Bedarf verbessern kann.

Sonst steht nichts darin. Kein Fülltext. An diesem Punkt laufen viele Gestalter leicht in eine Falle: Vor vielen, vielen Jahren gab es ein paar Screenreader, die Eingabefelder nur dann erkannten, wenn Text darin stand. Die haben sich verständlicherweise nicht lange gehalten. Deutlich länger freilich blieb die Idee, so ein Text sei hilfreich.

Der Text muß automatisch verschwinden, wenn man den Fokus ins Feld legt – sonst zwingt man den Besucher zu umständlichen Löscharbeiten. Also darf er nur per Javascript eingefügt werden, denn nur so funktioniert das automatische Löschen. Wer ohne Javascript auf die Seite kommt, hätte andernfalls ja doch die Löscherei von Hand zu erledigen.

Die Funktion des Feldes muß also schon ohne Javascript klar erkennbar sein. Ist das nicht der Fall, muß man das Formular entsprechend reparieren.

Wenn also ohne Javascript schon alles klar ist, brauchen wir auch den Fülltext nicht. Ich habe noch keine Seite gesehen, auf der dieser Text irgendeinen erkennbaren Mehrwert gebracht hat.

Das Attribut pattern beschreibt mit einem regulären Ausdruck, welche Zeichen erwartet werden. Hier möchte ich mindestens eines, denn sonst vergeudet der Leser nur seine Zeit.
Dieses Attribut ist im Grunde redundant, denn das nächste – required – verlangt auch mindestens ein Zeichen. Ich habe beide eingebaut, um denjenigen zu helfen, deren Browser nur eins erkennt.

Diese Attribute erzwingen eine clientseitige Validierung der Eingabe auch dann, wenn der Leser Javascript ausgeschaltet hat.

Der Absendebutton hat kein Attribut name. So wird dessen Wert auch nicht übertragen, und die URL für die Suche bleibt hübsch übersichtlich.

Javascript

Das Suchformular habe ich mit einer kleinen Funktion aufgepeppt:

function addEvent(target, eventType, functionRef, capture) {
    if (typeof target.addEventListener != "undefined") {
        target.addEventListener(eventType, functionRef, capture);
    } else if (typeof target.attachEvent != "undefined") {
        target.attachEvent("on" + eventType, functionRef);
    } else {
        eventType = "on" + eventType;
        if (typeof target[eventType] == "function") {
            var oldListener = target[eventType];
            target[eventType] = function() {
                oldListener();
                return functionRef();
            };
        } else {
            target[eventType] = functionRef;
        }
    }
}
function checkSearch() {
    var sform           = document.getElementById("searchform");
    var s               = document.getElementById('s');
    s.setAttribute('autosave', 'toscho.de');
    s.setAttribute('results', '15');
    var submit          = document.getElementById('searchsubmit');
    sform.onsubmit = function() {
        if ( '' == this.s.value ) {
            alert('Wenn Sie nach nichts suchen, werden Sie nichts finden.');
            s.focus();
            return false;
        }
        return true;
    };
}
addEvent(window, 'load', checkSearch);

Die erste Funktion addEvent() habe ich aus dem Javascript-Buch von SitePoint. Den Rest habe ich mir selbst zusammengeschustert. Ihr seht bestimmt, was ich alles besser machen kann.

Hier werden zunächst diese beiden Zeilen relevant:

s.setAttribute('autosave', 'toscho.de');
s.setAttribute('results', '15');

Die helfen den Nutzern webkit-basierter Browser wie Safari oder Google Chrome. Sie sagen: Merke dir automatisch die letzten 15 Einträge, und ordne sie der Domain toscho.de zu. Im Screenshot aus Safari 3 oben sieht man solch einen gespeicherten Ausdruck.
Das ist praktisch bei langen Worten oder solchen, die man leicht falsch schreibt. Noch praktischer wäre eine Methode, die Suchausdrücke ohne Treffer aus der Liste heraushalten könnte. Soweit ich weiß, gibt es die noch nicht. Korrigiert mich bitte!

Kurz darauf kommt noch eine kleine Kontrolle auf leere Einsendungen. Nicht jeder benutzt schließlich einen modernen Browser, der die neuen Attribute schon unterstützt.

Links

21 Kommentare

  1. molily am 08.01.2010 · 23:59

    So lässt sich übrigens mit der HTML5-Formularvalidierungs-API eine eigene Fehlermeldung umsetzen:

    var sform = document.getElementById("searchform");
    var s = document.getElementById('s');
    sform.onsubmit = s.oninput = function () {
       var validity = s.validity;
       if (validity.valueMissing || validity.patternMismatch) {
          s.setCustomValidity('Wenn Sie nach nichts suchen, werden Sie nichts finden.');
       } else {
          s.setCustomValidity('');
       }
    };

    Die wird im Opera in der Standard-Fehlermeldungsbox dargestellt. Indem man auf validity und setCustomValidity prüft, kann man einen Fallback mit alert() umsetzen. (Das nur perspektivisch.)

  2. [asterix] am 09.01.2010 · 19:47

    Zum Thema vordefinierte value-Attribute.

    Bei einem neuen Projekt von mir habe ich kürzlich eine eigene neue Methode ausprobiert. Ich konnte bisher noch keine schwerwiegenden Probleme feststellen. Sie funktioniert nur mit CSS in standardkompatiblen Browsern. Im Internet Explorer #searchform label {
    display: block;
    position: absolute;
    margin-top: 1em; /* Höhe des Suchfeldes */
    z-index: 10; /* Damit das Label nicht hinter das Eingabefeld gerät */
    }

    #searchform:focus label, #searchform label:focus {
    display: none; /* Ausblenden während Eingabe */
    }

    Ich werde noch etwas daran tüfteln, vielleicht wird ja mal etwas draus. Wer weiss. ☺

  3. Thomas Scholz am 09.01.2010 · 19:50

    @[asterix]: Wozu der Aufwand?

  4. [asterix] am 09.01.2010 · 20:15

    Damit's auch ohne JavaScript funktioniert. ☻

  5. [asterix] am 09.01.2010 · 20:16

    Der Code sollte eigentlich als Block-Code dargestellt werden, aber irgendwie hat mir dass dein Editor rausgeworfen. Ich denke, dass sollte nicht so sein, oder?

  6. Thomas Scholz am 09.01.2010 · 20:24

    @[asterix]: Vermutlich hast du ein nicht maskiertes < benutzt: Das frißt das Plugin, das mein Markup sauber hält, leider vor dem Eintrag in die Datenbank. Ich habe jetzt mal einen entsprechenden Hinweis über das Textfeld hier gesetzt.

    Damit's auch ohne JavaScript funktioniert. ☻

    Was funktioniert dann?

  7. [asterix] am 09.01.2010 · 20:34

    Es funktioniert ohne JavaScript, ein Label und ein Eingabefeld, beispielsweise auf engem Raum, kompakter Darzustellen. Es löst die «JavaScript/value-Attribut»-Methode ab.

  8. Thomas Scholz am 09.01.2010 · 20:40

    @[asterix]: Wozu brauchst du absolute Positionierung, um ein label unter das Eingabefeld zu stellen? Und wozu das label überhaupt? Das verstehe ich nicht.

  9. [asterix] am 09.01.2010 · 20:57

    Ich positioniere nicht unter sondern in das Eingabefeld (es soll dort das defaultValue-Attribut ablösen). Das Label wird quasi über das Suchfeld gelegt.

    Das Label steht im Quelltext über dem Suchfeld. Nun wird es mit CSS nach unten direkt in dieses gelegt. Durch die absolute Positionierung wird das Label vom Textfluss und dem Rest der Seite ignoriert. Damit wird verhindert, dass oben ein Platz frei bleibt, an welchem das Label vorher stand.

    Wieso ein Label? Mit CSS kann ich es nicht mit dem value-Attribut lösen. Deshalb habe ich zu einem Label gegriffen.

  10. Thomas Scholz am 09.01.2010 · 21:02

    @[asterix]: Strukturell brauchst du kein label, und visuell auch nicht. Obendrein mußt du jetzt noch den Fall abfangen, daß der letzte Suchausdruck im Eingabefeld steht, denn sicher möchtest du nicht zwei Texte übereinander legen. Wozu? Niemand hat einen Nutzen davon.

  11. [asterix] am 09.01.2010 · 21:29

    Ich denke, der Benutzer erkennt das Suchfeld nicht, wenn es nicht entsprechend beschriftet/gestaltet ist. Visuell braucht es also ein Label, einen Button… Irgendetwas eindeutiges. Zwar wäre input-type="search" eindeutig erkennbar, doch der Browser-Support fehlt in den meisten Fällen. Und ein leeres Eingabefeld ohne Beschriftung kann ich als Benutzer nicht als Suche erkennen.

    Es ist meines erachtens semantischer Labels zur Beschriftung zu verwenden als das «value»-Attribut.

    Beim Projekt, welches ich erwähnt habe, gibt es keinen Button «Suchen» sondern nur ein blankes Eingabefeld. Ohne ein passendes Label wäre da nicht zu erkennen, dass es sich um eine Suche handelt. Da ich, wie gesagt, nicht gerne «defaultValue» verwende, greife ich auf ein Label. Dieses ist wie oben erwähnt gestaltet.

    Bezogen auf deine Suche ist ein Label jedoch nicht nötig, da gebe ich dir recht.

  12. Thomas Scholz am 09.01.2010 · 21:42

    @[asterix]: Ach so, du redest von einem Feld ohne Button. Da muß die Beschriftung natürlich irgendwie erzeugt werden. Ich würde dann aber auf den Einsatz eines echten Buttons drängen, denn ohne den bekommt man im IE ernste Probleme.

  13. [asterix] am 09.01.2010 · 22:08

    @Thomas Scholz: Ja genau, es handelt sich um ein Feld ohne Button. Dass der IE damit Probleme hat, wusste ich nicht. Dann werde ich wohl doch einen Button einsetzen müssen.

  14. Bernhard Häussner am 10.01.2010 · 15:06

    Also Eingabefelder ohne Button finde ich ja doof... wenn man z.B. einen Suchbegriff pastet oder aus der Liste der letzen Suchbegriffe wählt, dann muss man ja erst wieder mit der Tastatur rummachen, um das Formular abzuschicken.

    Ich habe noch keine Seite gesehen, auf der dieser Text irgendeinen erkennbaren Mehrwert gebracht hat.

    Also ich sehe es bei mir schon als Mehrwert. Wenn kein Javascript aktiviert ist, ist es eine ganz normale Suche, wenn es aktiviert ist, steht im Eingabefeld Sofort-Suche, weil dann mittels AJAX sofort beim tippen gesucht wird.
    Das hat sogar den netten Nebeneffekt, dass ich auf den ersten Blick erkennen kann, ob Javascript aktivert ist. (Bzw. beim IE stelle ich immer mal fest, dass es einfach nicht klappt...)

  15. Francesco am 10.01.2010 · 17:41

    Kann man method="get" beim öffnenden form-Tag nicht einfach weglassen? Laut Spezifikation schon.

  16. Thomas Scholz am 10.01.2010 · 17:48

    @Francesco: Ja, kann man. Ich habe den Wert in manchen Themes jedoch auf POST stehen und leite dann auf /suche/suchwort um. Die explizite Notation hilft mir, gleich zu sehen, wie ich im aktuellen Theme verfahre. Wer mit diesem sicher recht exotischen Problem nicht konfrontiert wird, kann das Attribut weglassen.

  17. molily am 10.01.2010 · 23:05

    Wo ihr schon die ganze Zeit über Vorbelegung diskutiert: HTML5 bietet das placeholder-Attribut genau dafür. Damit ist kein JavaScript mehr nötig. Wundert mich, dass das hier noch niemand genannt hat, zumal es um HTML5-Formulare geht.

    Jetzt kann man sich natürlich noch darüber streiten, dass placeholder ja kein Label-Ersatz, sondern höchstens eine Ergänzung ist... Auf manchen Sites habe ich mit JavaScript sozusagen eingebettete Labels benutzt und die Labels barrierefrei ausgeblendet.

  18. Bernhard Häussner am 11.01.2010 · 00:25

    @molily: Stimmt, das wurde im Blogeintrag überhaupt nicht erwähnt. Ich style gerne den "palceholder" ein bisschen unauffälliger, als den eingetippten Text. Ich wundere mich, wie das mit diesem HTML5-Attribut klappen würde. Klar kann das immer nur ergänzend zum Label sein, aber z.B. für Beispieldaten könnte es Sinn haben. Nur dann müsste es eben auch als Beispiel erkennbar sein, z.B. durch schwächere Farbe.

  19. Thomas Scholz am 11.01.2010 · 07:26

    @molily: Ich hatte es komplett vergessen. Danke!

    @Bernhard Häussner: Wenn placeholder verwendet wird, bleibt value ja initial leer. Also kannst du per Javascript prüfen, ob das value leer ist und dem input die passende Farbe geben.

    Ich bin mir nicht ganz sicher, wie der Selector ::value auf ein leeres Attribut anspricht (und welcher Browser das schon implementiert hat). Denkbar wäre irgendwann eine reine CSS-Lösung:

    input:not(::value)
        {
            color:   #999;
        }
  20. Thomas Scholz am 12.01.2010 · 16:34

    Man kann den Platzhalter in Webkit (sonst versteht das ohnehin niemand) sogar per CSS ansprechen:

    input::-webkit-input-placeholder {}

    Nicht gerade schön, aber immerhin: Es geht.

  21. Francesco am 19.04.2010 · 09:48

    Mit einem beherzten input[type="search"] { -webkit-appearance: none; } kann man übrigens die standardmäßige Erscheinung des Suchfelds (runde Ecken, etc.) deaktivieren und sein eigenes CSS einsetzen. Die Lupe mit dem Pfeil nach unten bleibt (praktischerweise) trotzdem erhalten.