molily Navigation

Strenge HTML-4-Validierung

In Anschluss an meinen letzten Artikel zu XHTML möchte ich die Verfahren erläutern, die ich zur HTML-4-Qualitätssicherung genutzt habe.

Vorab sei noch einmal betont: SGML-Validierung halte ich zur Überprüfung von Markup-Qualitätsstandards für ungeeignet. Deshalb empfehle ich nicht den Einsatz von HTML 4, sondern rate zu einem Wechsel zu HTML 5 oder XHTML 1, welche sich beide mit strengeren Parsern prüfen lassen. Hier geht es also um die Qualitätssicherung bei »Altlasten«, also den zahlreiche bestehenden HTML-4-Dokumenten, die sich womöglich nicht so einfach migrieren lassen.

Wie schon angedeutet nehmen wir Änderungen in der SGML-Deklaration für HTML sowie der Dokumenttyp-Deklaration (DTD) für HTML 4.01 Strict vor und nutzen dann einen SGML-Parser zur Validierung.

Zuerst einmal schalten wir zwei SGML-Features in der SGML-Deklaration aus, die aus meiner Sicht der Markup-Qualität entgegenstehen.

OMITTAG: Zwingend erforderliche Start- und End-Tags

In der SGML-Deklaration setzen wir das Kurzschreibung-Feature OMITTAG YES auf OMITTAG NO. OMITTAG (zu deutsch Tag weglassen) bedeutet, dass die Tags von nicht-leeren Elemente in der DTD als optional angegeben werden können. In HTML 4 sind bei einigen wichtigen Elementen Start- und End-Tag optional. Beispiele aus der DTD:

<!ELEMENT HTML O O (%html.content;)    -- document root element -->
<!ELEMENT BODY O O (%block;|SCRIPT)+ +(INS|DEL) -- document body -->
<!ELEMENT HEAD O O (%head.content;) +(%head.misc;) -- document head -->
<!ELEMENT TBODY    O O (TR)+           -- table body -->

»O O« heißt hier: Start-Tag optional und End-Tag optional. Das ist besonders tückisch: Die Tags sind zwar weglassbar, die Elemente sind aber trotzdem da und werden vom Parser automatisch hinzugefügt. Das hat stets zu Verwirrungen geführt. Dass eine Tabelle immer ein tbody-Element hat, auch wenn man keines notiert, merkt man spätestens dann, wenn man mit JavaScript versucht, direkt tr-Elemente an das table-Element zu hängen. Das geht nämlich nicht, weil ein tbody-Element (oder thead/tfoot) zwingend notwendig ist - in der HTML-Serialisierung des Dokuments darf es jedoch »unsichtbar« sein, indem seine Tags optional sind.

Es gibt viel mehr Elemente in HTML, deren Start-Tags notwendig, deren End-Tags hingegen optional sind. Einige Beispiele:

<!ELEMENT P - O (%inline;)*            -- paragraph -->
<!ELEMENT (TH|TD)  - O (%flow;)*       -- table header cell, table data cell-->
<!ELEMENT LI - O (%flow;)*             -- list item -->

Es sind also folgende Schreibweisen erlaubt:

<p>Absatz
<p>Absatz
<ul>
	<li>Eins
	<li>Zwei
</ul>

Der Konsistenz halber verbieten wir diese Kurzschreibweise und machen alle Start- und End-Tags von nicht-leeren Elementen zwingend erforderlich - wie in XHTML.

Dazu ändern wir die angesprochenen »O O« sowie »- O« bei Elementen, die nicht das Inhaltsmodell EMPTY haben, zu »- -«:

Alt:  <!ELEMENT HTML O O (%html.content;)    -- document root element -->
Neu:  <!ELEMENT HTML - - (%html.content;)    -- document root element -->

Alt:  <!ELEMENT P - O (%inline;)*            -- paragraph -->
Neu:  <!ELEMENT P - - (%inline;)*            -- paragraph -->

SHORTTAG: Praxisuntaugliche Kurzschreibung verbieten

Für HTML ist ein verhängnisvolles SGML-Feature namens SHORTTAG eingeschaltet. SHORTTAG erlaubt theoretisch kryptische Kurzschreib-Möglichkeiten von Elementen. Die werden in der Praxis aber überhaupt nicht unterstützt (weil die Browser keine echten SGML-Parser, sondern Tag-Soup-Parser besitzen). Das Problem ist, dass man manchmal unabsichtlich dieses Feature benutzt. Das führt in der Praxis zu einem Fehler, ohne dass ein normaler HTML-Validator das ankreiden würde.

SHORTTAG ist auch der Grund, warum HTML 4.01 aus der theoretischen SGML-Sicht nicht mit XHTML 1.0 kompatibel ist. Und einer der Gründe, warum man X(HT)ML auf keinen Fall mit einem SGML-Parser verarbeiten sollte. Das ist aber Stoff für einen eigenen Artikel und wird im verlinkten Text von Jukka Korpela gut erklärt.

SHORTTAG erlaubt folgendes:

Minimization-Typ Kurzschreibweise Langschreibweise
Leere Tags ohne Elementname <> ... </> <?> ... <?> (welcher Elementname genommen wird, hängt vom Kontext ab)
Tags ohne schließendes > <p<strong>wichtig</strong></p> <p><strong>wichtig</strong></p>
Null End-Tags (NET) und NET-enabling Start-Tags <input/ <input>
<p/Dies ist ein Textabsatz/ <p>Dies ist ein Textabsatz</p>
Attributnamen und -begrenzer weglassbar <input submit> <input type="submit">
<form action=beispiel.php POST> <form action="beispiel.php" method="POST">

Hintergrundwissen: SGML and HTML Explained. Chapter 9: Tag Minimization von Martin Bryan

Christoph Schneegans hat all diese Features in einem Beispieldokument veranschaulicht, um die Probleme von HTML 4 aufzuzeigen.

Jetzt, wo wir wissen, dass SHORTTAG gefährlich ist, schalten wir es in der SGML-Deklaration ab. Wir ändern die Zeile

SHORTTAG YES

zu

SHORTTAG
  STARTTAG
    EMPTY    NO
    UNCLOSED NO
    NETENABL NO
  ENDTAG
    EMPTY    NO
    UNCLOSED NO
  ATTRIB
    DEFAULT  YES
    OMITNAME NO
    VALUE    NO

Es werden alle SHORTTAG-Unterfeatures abgeschaltet (NO) bis auf eines: ATTRIB VALUE, das ist die Möglichkeit, in der DTD einen Default-Wert für ein Attribut anzugeben. Davon macht die HTML-4-DTD Gebrauch und wir wollen dieses Feature nutzen. Sonst könnten wir nicht <td> schreiben, sondern müssten immer das explizit <td rowspan="1"> notieren (wenn ich das richtig überblicke).

Vorschläge für weitere individuelle Striktheiten

Es macht Sinn, sich selbst noch weitere Coding-Konventionen aufzuerlegen und Teile des Vokabulars aus der DTD zu streichen:

  • Die Elemente tt, i, b, big und small gehören zu HTML 4.01 Strict, obwohl sie direkt die Präsentation beeinflussen. Dasselbe gilt für Attribute wie border bei table und align/valign bei th/td.
  • Wenn man Unobtrusive JavaScript schreiben will, gilt es Inline-Event-Handler wie onmouseover und das Element noscript zu vermeiden.
  • Um div-Suppe vorzubeugen, kann man das Inhaltsmodell von div so verändern, dass es nur Blockelemente enthalten darf, sodass div nur als Container genutzt werden kann.

Das nur als Ideen für mögliche Zusatzregeln.

OpenSP zur Validierung nutzen

Nachdem wir SGML-Deklaration und HTML-4.01-DTD entsprechend angepasst haben, müssen wir uns einen validierenden SGML-Parser aufsetzen, der Dokumente anhand der definierten Grammatik prüft. Der hier beschriebene Weg nutzt ein Kommandozeilenprogramm, das für viele Betriebssysteme verfügbar ist: Der SGML-Parser onsgmls, Teil von OpenSP.

Ich werde im Folgenden die Windows-Variante näher beschreiben, habe das Schema aber auch unter Linux erfolgreich angewandt.

  1. Download der OpenSP-Binaries für Windows
  2. Das ZIP mit den Binaries entpacken. Unter bin/ finden wir die Binary (ausführbare EXE-Datei) onsgmls.exe und die zugehörige DLL-Bibliothek osp152.dll. Diese kopieren wir in einen neu angelegten Ordner.

Nun brauchen wir eine Reihe von aufeinander verweisende Dateien, damit ein gewöhnliches HTML-4.01-Strict-Dokument gegen unsere angepasste DTD samt angepasster SGML-Deklaration validiert werden kann.

stricter-html4.socKatalog mit Verweis auf SGML-Deklaration und Mapping der Public-Idenfitier auf DTD-Dateien
stricter-html4-sgml.dclAngepasste SGML-Deklaration
stricter-html401-strict.dtdAngepasste HTML-4.01-Strict-DTD
HTMLlat1.entZeichen-Entities für die DTD
HTMLsymbol.entZeichen-Entities für die DTD
HTMLspecial.entZeichen-Entities für die DTD
  1. All diese Dateien habe ich zum Download in einer ZIP-Datei zusammengefasst.
  2. Das ZIP-Datei entpacken wir der Einfachheit halber in den Ordner, den wir oben für ngsmls.exe und Co. angelegt haben
  3. Wir starten eine Shell (Eingabeaufforderung, Powershell oder Bash) und wechseln mit cd in das Verzeichnis, in dem das zu validierende Dokument liegt.
  4. Angenommen, onsgmls.exe liegt unter c:\Users\molily\projekte\validierung\ und das zu prüfende Dokument lautet dokument.html, so starten wir den Parser mit:

    c:\Users\molily\projekte\validierung\onsgmls.exe -cc:\Users\molily\projekte\validierung\stricter-html4.soc -wvalid -E0 -s dokument.html

Die ausgegebenen Fehlermeldungen sind leider äußerst kryptisch. Wenn wir <td align=center> (ohne Anführungszeichen als Attributbegrenzer) notieren, was die geänderte DTD als Fehler ansieht, so wirft der SGML-Parser folgenden Fehler aus:

c:\Users\molily\projekte\validierung\onsgmls.exe:dokument.html:6:21:E: an attribute value
   specification must be an attribute value literal unless SHORTTAG YES is specified

dokument.html:6:21 heißt hier Zeile 6 in Spalte 21 in der Datei dokument.html.

Das manuelle Aufrufen über die Kommandozeile ist alles andere als komfortabel. Um den Aufruf zu vereinfachen, könnte man sich eine Batch-Datei (oder ein Script in Perl, PHP, Ruby, Python) schreiben, das sich einfacher aufrufen lässt, sodass man bspw. nur noch html4valid dokument.html eintippen muss. Ich hatte mir eine PHP-Datei geschrieben, die rekursiv ein Verzeichnis nach HTML-Dateien durchsucht und alle darin validiert. Validate your markup with NSGMLS beschreibt, wie nsgmls in verschiedene Editoren als externes Tool eingebunden werden kann.

Fazit

Das soll nur grob den Weg skizzieren, den ich zur Qualitätssicherung gewählt hatte. Über Korrekturen und Ergänzungen freue ich mich, am besten per Mail.

Wie gesagt wäre noch viel zu tun, um von einem solchen Proof-of-Concept zu einem praktisch vielseitig einsetzbaren Tool zu kommen. Es liegt jedoch nicht (mehr) in meinem Interesse, einen Web-Service um eine solche SGML-Validierung herum zu bauen (z.B. mit dem W3C-Validator), da ich keine Zukunftshoffnungen HTML 4 in setze. Mir bedeutet die Qualitätssicherung von »Altlasten« zunehmend weniger und ich halte ich eine Migration zu (X)HTML 5 für angebrachter.

Ein ähnlicher Ansatz: Validating against a custom DTD von Spartanicus.