Specyfikacja#
DTD#
Definicję typu dokumentu (DTD) opisałem już w rozdziale "Doctype". Były to podstawowe informacje dla początkujących webmasterów, dzięki którym mogli oni tworzyć strony zgodne z ogólnie przyjętymi, jak najlepszymi praktykami. W tym miejscu poświęcę więcej czasu na samą budowę DTD, dzięki czemu samodzielne czytanie pliku nie będzie stanowiło problemów.
Umiejętność czytania DTD może nam zastąpić każdy wykaz elementów podawany na stronach poświęconych językowi HTML. Wystarczy mieć plik DTD pod ręką a składnia HTML-a nie ukryje przed nami żadnych tajemnic.
Podstawy#
DTD (Document Type Definition) jest rodzajem dokumentu, który definiuje formalną strukturę innych dokumentów XML, XTHML, HTML lub innych pochodzących z rodziny SGML lub XML. Definicje DTD mogą być zawarte bezpośrednio w danym dokumencie, którego strukturę definiują, ale zazwyczaj zapisywane są w osobnym pliku tekstowym, co pozwala na zastosowanie tego samego DTD dla wielu dokumentu (analogicznie jak w przypadku zewnętrznych arkuszy stylów czy skryptów).
DTD określa składnię konkretnej aplikacji XML lub SGML, np. HTML, EAD, TEI lub innej, zdefiniowanej dla potrzeb użytkowników. Zazwyczaj DTD definiuje każdy dopuszczalny element dokumentu, jego zbiór atrybutów i dopuszczalne wartości. DTD określa także zagnieżdżanie i wymagalność poszczególnych elementów w dokumencie.
DTD powinien dostarczyć aplikacji klienckiej (np. przeglądarce internetowej) istotnych informacji o składni języka użytego do utworzenia dokumentu. Na jego podstawie przeglądarka powinna wiedzieć, w jaki sposób przetworzyć dany dokument. W praktyce jednak przeglądarki mają domyślnie zdefiniowane reguły przetwarzania dokumentów i żadna deklaracja DTD na początku strony tego nie zmienia.
Sam DTD jako schemat opisu dokumentów ma małe możliwości. Istnieją dużo lepsze rozwiązania np. XML Schema czy RelaxNG.
Wewnętrzny DTD#
Jeśli definicja DTD znajduje się wewnątrz dokumentu powinna być umieszczana według następującego schematu:
<!DOCTYPE root-element [element-declarations]>gdzie "
root-element
" definiuje korzeń dokumentu (czyli pierwszy nadrzędny element). W miejsce "element-declarations
" wpisywane są poszczególne definicje dla każdego elementu.Prosty przykład poprawnego DTD:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE osoba [
<!ELEMENT osoba (imie, drugieImie, nazwisko)>
<!ELEMENT imie (#PCDATA)>
<!ELEMENT drugieImie (#PCDATA)>
<!ELEMENT nazwisko (#PCDATA)>
]>
<osoba>
<imie>Grzegorz</imie>
<drugieImie>Stachu</drugieImie>
<nazwisko>Brzęczyszczykiewicz</nazwisko>
</osoba>
Działanie powyższego przykładu najlepiej przetestować za pomocą pliku testowego. By odczytać DTD wystarczy wybrać z menu kontekstowego przeglądarki "Pokaż źródło strony
".
Zewnętrzny DTD#
Oprócz metody umieszczania DTD bezpośrednio w dokumencie można także stosować odwołanie do zewnętrznego pliku na dwa sposoby:
- identyfikator systemowy
<!DOCTYPE root-element SYSTEM "scieża_dostępu_DTD">
- identyfikator publiczny
<!DOCTYPE root-element PUBLIC "FPI">
gdzie "FPI" (Formal Public Identifier) jest formalnym publicznym identyfikatorem, określony w normie ISO 9070. FPI tworzony i upowszechniany jest przez różne organizacje pracujące nad językami znacznikowymi (np. W3C dla HTML czy XHTML).
Prosty przykład dla zewnętrznego DTD:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE osoba SYSTEM "external.dtd">
<osoba>
<imie>Grzegorz</imie>
<drugieImie>Stachu</drugieImie>
<nazwisko>Brzęczyszczykiewicz</nazwisko>
</osoba>
Mieszany DTD#
Możliwe jest także połączenie zewnętrznego i wewnętrznego DTD:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" [
<!-- wewnętrzne deklaracje -->
]>
<html>
...
</html>
Co ciekawe, DTD może w ogóle nie zawierać żadnych deklaracji:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html>
<html>
...
</html>
W takie sytuacji DTD po prostu określa, że dokument ma jeden element najwyższego poziomu (single top-level element).
Jednostki leksykalne DTD#
W opisie DTD języka HTML występują cztery rodzaje zapisów:
- komentarze
ENTITY
- makrodefinicja, która skraca całą specyfikację czyniąc ją bardziej czytelnąELEMENT
- definicja elementu lub grupy elementów języka HTML.ATTLIST
- definicja dopuszczalnych atrybutów dla elementu lub grypy elementów.
Zapisy ELEMENT
oraz ATTLIST nazywa się deklaracjami, natomiast ENTITY
- makrodefinicjami.
Komentarze#
Jak w każdym języku programowania lub opisu dokumentów, komentarze przydają się do słownego wyjaśnienia danego fragmentu. W DTD komentarze umieszczamy pomiędzy parą znaków ("--") a ("--") . Prosty przykład:
<!
-- pierwszy poprawny komentarz DTD --
--
drugi
poprawny
komentarz DTD
--
>
Komentarze mogą rozciągać się na wiele linii. Mają charakter tylko i wyłącznie informacyjny.
Makrodefinicje ENTITY#
Makrodefinicje pozwalają zdefiniować pewien tekst (w krótszej formie), którego rozwinięcie będzie umieszczone w innym miejscu DTD. Dzięki nim cała definicja staje się zwięzła i bardziej czytelna. Wszystkie makra podzielono na podstawowe grupy i umieszczono na początku DTD. Krótki przykład:
<!ENTITY % heading "H1|H2|H3|H4|H5|H6">
Powyższy zapis jest makrodefinicją ustalającą znaczenie napisu % heading
.
Makrodefinicja rozpoczyna się od znaków <!ENTITY %
po których występuje nazwa, następnie wewnątrz cudzysłowów "..." umieszcza się rozwinięcie nazwy, po czym zamyka całe polecenie znakiem >
.
Wywołanie takiej makrodefinicji w DTD następuje poprzez użycie znaku procenta ("%"), po którym podaje się nazwę makrodefinicji, i całą konstrukcję kończy znakiem średnika (";"), przykładowo:
<!ELEMENT (%heading;) - - (%inline;)* -- heading -->
Wewnątrz rozwinięcia nazwy makrodefinicji (między cudzysłowami) mogą pojawić się odwołania do innych makrodefinicji, np:
<!ENTITY % inline "#PCDATA | %fontstyle; | %phrase; | %special; | %formctrl;">
Najczęściej stosowanymi makrami będą definicje dzielące elementy HTML na dwie grupy: elementy tekstowe - %inline;
, oraz elementy blokowe - %block;
, jak również trzy główne grupy atrybutów %coreattrs;
, %i18n;
i %events;
.
Czytanie DTD z wieloma makrodefinicjami może nieco męczyć, ze względu na umieszczenie makr na samym początku DTD. Najlepiej otworzyć sobie dwa osobne okna z plikiem DTD, w pierwszym analizujemy konkretny fragment, a w drugim mamy podgląd na makra.
Oczywiście makrodefinicje DTD nie mogą pojawić się w treści dokumentów xHTML. Pewnym rodzajem makr dla tych plików będą encje, które szczegółowo opisałem wcześniej. Same encje dla HTML-a zostały zadeklarowane w osobnych plikach, w których zastosowano podobną składnię z DTD. Krótki przykład:
<!ENTITY lt CDATA "<" -- less-than sign, U+003C ISOnum -->
Deklaracje ELEMENT#
Większa część DTD dla HTML-a składa się z deklaracji elementów oraz ich atrybutów. Deklaracja elementu rozpoczyna się od znaków <!ELEMENT
a kończy znakiem >
. Wewnątrz niej muszą zostać określone:
- Nazwa elementu.
- Czy znaczniki otwierający i zamykający są dopuszczalne, wymagane czy opcjonalne. Oznacza się to przez umieszczenie konkretnych znaków zaraz po nazwie. Oto możliwości:
- "- -" - dwa minusy oznaczają, że znaczniki otwierający i zamykający są obowiązkowe.
- "- O" - jeden minus, po którym następuje litera "O" oznacza, że znacznik otwierający jest obowiązkowy a znacznik zamykający jest opcjonalny
- "- O" EMPTY - wzmocniona zasada, która mówi, że znacznik zamykający musi zostać pominięty.
- "O O" - dwie następujące po sobie litery "O" oznaczają, że znaczniki otwierający i zamykający są opcjonalne.
- Zawartość elementu (jeśli w ogóle posiada). Poprawna zawartość dla elementu określana jest mianem wzorca zawartości (
content model
). Elementy, które zostały zdefiniowane bez żadnej zawartości nazywa się elementami pustymi. Zawartość takiego elementu deklarowana jest przez słowoEMPTY
.
Oto prosty przykład definiujący obraz:
<!ELEMENT IMG - O EMPTY -- Embedded image -->
Polecenie deklaruje element IMG
, który jest pusty (nie ma żadnej zawartości), dodatkowo znacznik zamykający jest zabroniony.
Definicja wzorca zawartości (content model)#
Wzorzec zawartości definiuje prawidłową zawartość każdego elementu. Może obejmować:
- Dozwolone lub zabronione nazwy elementów (np. element
UL
moze zawierać elementLI
, a elementP
nie może zawierać innego elementuP
). - Makrodefinicje (np. element
LABEL
może zawierać rozwinięcie z%inline;
) - Tekst dokumentu (
#PCDATA
będący konstrukcją SGML). W tekście mogą pojawić się encje HTML-a.
Wzorzec zawartości można tworzyć za pomocą wielu operatorów, które pozwalają ustalić liczbę powtórzeń (kardynalność) dla elementu (lub elementów). Operatory można używać także przy atrybutach. Poniżej zamieszczam możliwe operatory dla DTD w HTML-u:
Zapis | Znaczenie |
---|---|
(...) | Grupowanie |
A | Może wystąpić dokładnie jeden raz |
A+ | Może wystąpić jeden lub więcej razy |
A? | Może wystąpić jeden raz lub nie wystąpić wcale |
A* | Może wystąpić zero lub więcej razy |
+(A) | Może wystąpić |
-(A) | Nie może wystąpić |
A | B | Musi wystąpić dokładnie jeden element: A lub B (nie oba, nie żaden, a dokładnie jeden z nich) |
A , B | Muszą wystąpić oba elementy A i B w dokładnie takiej kolejności |
A & B | Muszą wystąpić oba elementy A i B w dowolnej kolejności |
Powyższa lista jest uproszczeniem stosowania pełnej składni SGML.
Rozważmy przykład:
<!ELEMENT (%heading;) - - (%inline;)* -- heading -->
Jest to deklaracja nagłówków H1, ..., H6
. Makrodefinicja %heading;
zostanie rozwinięta do "H1|H2|H3|H4|H5|H6
". Zatem definiowanymi elementami są H1, ..., H6
. Znak "|
" użyty w makrodefinicji jest znakiem alternatywy, zaś nawiasy okrągłe otaczające makro (%heading;)
są użyte do grupowania. Innymi słowy deklaracja określa każdy z elementów H1, ..., H6
z osobna (można ją interpretować identycznie jak sześć niezależnych deklaracji postaci <!ELEMENT H1 .... >
, <!ELEMENT H2 .... >
, itd.)
Jeśli chodzi o znaczniki otwierający i zamykający każdego elementu H1, ..., H6
to są one wymagane. Decyduje o tym napis "- -
".
Nawiasy okrągłe (%inline;)*
grupują elementy, zaś gwiazdka określa, że elementy zawarte w nawiasach mogą wystąpić dowolna liczbę razy. Zatem zapis mówi, że poprawną zawartością elementów H1, ..., H6
jest dowolna liczba, tj. zero, jeden lub więcej, napisów określonych makrodefinicją %inline;
.
Kolejny przykład:
<!ELEMENT UL - - (LI)+ -- unordered list -->
Definiuje on element UL
. Znacznik otwierający i zamykający są obowiązkowe ("- -
"), zaś poprawną zawartością jest jeden lub więcej elementów LI
. Jak widzimy, znak "+" może wystąpić zarówno przed nawiasem otwierającym, jak i za nawiasem zamykającym (LI)+
.
Ostatni przykład:
<!ELEMENT A - - (%inline;)* -(A) -- anchor -->
Deklaracja oznacza, że element A
posiada dwa wymagane znaczniki, otwierający i zamykający. Poprawna zawartość jest zdefiniowana jako (%inline;)* -(A)
. Zatem może zawierać dowolna liczbę elementów typu %inline;
, natomiast nie może zawierać elementu A
(element A
jest zawarty w zbiorze elementów %inline;
). Jawne wykluczenie ma większą wagę od dołączania.
Kilka typów elementów HTML musi zawierać wykluczenia, jednym z nich jest formularz:
<!ELEMENT FORM - - (%block;|SCRIPT)+ -(FORM) -- interactive form -->
Deklaracje ATTLIST#
Definicja ATTLIST
ustala zbiór dopuszczalnych atrybutów elementu. Rozpoczyna się od napisu <!ATTLIST
, a kończy znakiem >. Wewnątrz niej muszą zostać określone:
- Nazwa atrybutu.
- Typ wartości atrybutu lub wyraźny zestaw możliwy wartości. Wartości nie są czułe na wielkość znaków.
- Dodatkowo można określić następujące opcje:
#IMPLIED
- atrybut opcjonalny, jeśli nie zostanie podany w dokumencie HTML, to ustaleniem jego wartości zajmuje się aplikacja kliencka (w niektórych przypadkach poprzez dziedziczenie z elementów nadrzędnych)#REQUIRED
- atrybut wymagany#FIXED
- wartość atrybutu jest ustalona na stałeVALUE
- niektóre definicje atrybutów wyraźnie określają wartość domyślną
Krótki przykład:
<!ATTLIST BR %coreattrs; -- id, class, style, title -->
Powyższe polecenie definiuje atrybuty dla elementu BR
. W komentarzu zawarto nazwy możliwych atrybutów, ale żeby stwierdzić, jakie mogą przyjąć wartości należy sprawdzić makrodefinicję %coreattrs;
.
Kolejny przykład:
rowspan NUMBER 1 -- number of rows spanned by cell --
http-equiv NAME #IMPLIED -- HTTP response header name --
id ID #IMPLIED -- document-wide unique id --
valign (top|middle|bottom|baseline) #IMPLIED
Atrybuty logiczne#
Niektóre atrybuty mogą występować w postaci logicznej (np. atrybut selected
w elemencie OPTION
). Ich pojawienie się w znaczniku otwierającym elementu oznacza, że wartość tego atrybutu jest "true
". Ich brak oznacza wartość "false
".
Logiczne atrybuty mogą legalnie przyjąć jedną wartość: nazwę samego atrybutu (np. selected
="selected
"). Oto przykład deklaracji takiego atrybutu:
selected (selected) #IMPLIED -- option is pre-selected --
W HTML-u atrybut logiczny można zadeklarować następująco (pełna forma):
<option selected="selected">
...
</option>
Możliwe jest też podanie alternatywnego zapisu (forma skrótowa):
<option selected>
...
</option>
Typy danych w DTD#
W języku HTML stosujemy kilka podstawowych rodzajów danych. Wartość atrybutu href=""
dla elementu A
jest innego typu niż na przykład wartość atrybutu class=""
znacznika H1
.
W celu odróżnienia wartości atrybutów wprowadzono typy danych oraz pewne makrodefinicje. Oto dostępne możliwości w DTD:
CDATA
(character data) - zwykły ciąg znaków, który dodatkowo może zawierać encje (bez kodu HTML). Aplikacje klienckie powinny interpretować wartości atrybutów w następujący sposób:- podmiana encji na właściwe znaki
- ignorowanie nowych linii
- podmiana wszystkich powrotów karetki oraz tabulatorów na pojedynczą spację
Aplikacje klienckie mogą ignorować początkowe i końcowe spacje w wartości CDATA atrybutów (np. "
myval
" może być interpretowane jako "myval
"). Autorzy nie powinni deklarować wartości atrybutów z niepotrzebnymi spacjami.Dla niektórych atrybutów HTML 4 z wartością atrybutu w postaci CDATA, specyfikacja nakłada kolejne ograniczenia poprawnych wartości dla atrybutu, które nie mogą być wyrażone poprzez DTD.
Chociaż elementy
STYLE
orazSCRIPT
używają wzorca zawartości CDATA, dla tych elementów CDATA musi być traktowane przez aplikacje klienckie w odmienny sposób. Cała zawartość, znaczniki oraz encje zostają przekazane do odpowiednich modułów przetwarzających w surowej postaci. Pierwsze wystąpienie ciągu znaków "</" (początek tagu zamykającego) jest traktowany jak zakończenie elementu, w którym się znajduje (czyli stylu lub skryptu). Jest to szczególnie widoczne i problematyczne w skryptach osadzanych bezpośrednio w kodzie HTML:... <script type = "text/javascript"> var opis = "To jest początek </ tagu zamykającego"; </script> ...
Powyższy przykład jest błędny, należy go zapisać w następującej postaci:
... <script type = "text/javascript"> var opis = "To jest początek <\/ tagu zamykającego"; </script> ...
Stosujemy tutaj tzw. znak uniku (character escape), który dla JavaScript można wprowadzić za pomocą symbolu ("\") - jest to backslash, odwrócony ukośnik.
#PCDATA
(parser character data) - dane tekstowe w HTML.Przykładem może być element
TITLE
:<!ELEMENT TITLE - - (#PCDATA) -(%head.misc;) -- document title -->
Tytuł nie powinien wystąpić w treści dokumentu, należy go wyświetlić np. jako nagłówek strony lub tytuł okna.
ID
# iNAME
- to łańcuchy znakowe, które rozpoczynają się od dużej lub małej litery łacińskiej (w notacji wyrażeń regularnych:[a-zA-Z]
), po której następuje dowolna liczba cyfr, liter, myślników, podkreśleń, dwukropków i kropek.IDREF
# iIDREFS
- odwołania do identyfikatorówID
zdefiniowanych w innych atrybutach.IDREF
jest pojedynczym łańcuchem znakowym,IDREFS
to lista wartości rozdzielonych spacjami.NUMBER
- niepusty ciąg cyfr.
Najwięcej niejasności może wystąpić przy analizie CDATA
oraz #PCDATA
. CDATA
występuje przy określaniu większości makrodefinicji oraz atrybutów. #PCDATA
występuje tylko przy deklaracji niektórych elementów (TITLE
, OPTION
, TEXTAREA
, FIELDSET
) oraz makrodefinicji (% inline
).
Analizując dłużej pojęcia CDATA
i #PCDATA
ustaliłem, że terminy te mogą mieć inne znaczenie w przypadku DTD, a inne w przypadku XML-a. Przyznam szczerze, że dokładnej różnicy nie udało mi się wychwycić. Można powiedzieć, że CDATA
oznacza zwykły test w makrodefinicji DTD, oraz zwykły tekst w atrybutach HTML. #PCDATA
oznacza zwykły tekst w elementach HTML. Pisząc "zwykły tekst" mam na myśli ciąg znaków, w którym nie mogą pojawić się znaczniki HTML.
Wszystkie powyższe typy danych będą przewijały się w całym DTD. Zdefiniowano wiele makrodefinicji, których rozwinięciem jest po prostu CDATA
, np: %ContentType;
, %ContentTypes;
, %Charset;
, %Charsets;
, %Character;
, %LinkTypes;
, %MediaDesc;
, %Datetime;
, %Script;
oraz %Text
. Zapamiętajmy zatem, że wymienione makra, w pewnym uproszczeniu, stanowią po prostu tekst nie zawierający kodu HTML.
Podsumowanie#
Tyle jeśli chodzi o budowę i interpretację poleceń w DTD. Nie będę opisywał poszczególnych makr, które grupują różne atrybuty, elementy i inne makra. To wszystko można samodzielnie odczytać i przeanalizować znając podstawy składni DTD.
Sam osobiście nigdy nie zaglądałem do definicji dokumentu - był to duży błąd. W jednym pliku mamy zapisaną całą składnię HTML-a, nie potrzeba dziesiątek książek, kursów i innych materiałów. Wystarczy nauczyć się budowy pliku, a odszukanie wymaganych atrybutów, ich wartości, dozwolonej zawartości elementów zajmuje dosłownie chwilkę.