Трепанация DTD

13 октября 2008 года, 07:58

Человеку присуща слабость всё упорядочивать и систематизировать. Заметно, что при этом становится гораздо удобнее воспринимать информацию, которая до этого казалась какой-то разрозненной и несвязанной кипой слов или цифр. Простой процесс приводит к довольно масштабному результату.

В Web-инженеринге также не обошлось без упорядочивания и систематизации. Одним из примеров подобного явления в нашей с вами области является DTD, который раскладывает по полочкам набор известных (X)HTML-тегов, не даёт запутаться валидатору и браузеру, а также помогает программисту, в некотором роде предоставляя для него минимальный набор справочного систематизированного материала. Жуть как удобно, правда?

Цели DTD

Конечно же, DTD создан не просто так. Корни данного формата уходят ещё в SGML. Можно даже сказать иначе: DTD является обязательной частью приложения SGML (коим является и HTML, к примеру). В нём объявляются и описываются всевозможные правила, применимые именно к данному языку разметки. И правда: у HTML свои правила, у XHTML уже немного другие. Даже эту разницу необходимо где-то отразить, чтобы было видно, что это не одно и то же.

Разумеется, DTD является не просто контрактом между разными программистами: DTD — это надёжная опора для валидаторов и браузеров. Да, именно DTD используют валидаторы для того, чтобы проверить документ на соответствие всем необходимым требованиям. Браузер же в меньшей степени использует данный документ.

Сформулируем основные цели, которые преследует DTD:

  1. Контракт между программистами;
  2. Описание набора правил, соответствующих для конкретного языка разметки;
  3. Самодокументация набора правил;
  4. Обособление различных приложений SGML (HTML, XHTML и так далее);
  5. Унификация способов записи правил разметки на разных языках.

Каждое правило тем или иным образом пересекается с другими правилами, таким образом образуя одну большую структуру данных описательного характера.

DTD в гипертекстовых языках разметки

Каждый Web-инженер встречался с таким явлением, как DOCTYPE. Все мы начинаем наши документы именно с объявления типа документа, чтобы браузеру было понятно, какой именно набор правил мы используем в разрабатываемом документе. Взглянем на различные DOCTYPE, существующие в XHTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">

Все три декларации типа документа состоят из 5 частей, каждая из которых отделена от соседних пробелами. Первые три части — DOCTYPE, html и PUBLIC — постоянны для HTML, а остальные варьируются в зависимости от используемого набора правил, применяемого к документу. Судя по данным декларациям, существуют три типа правил для HTML-документов: Strict, Transitional и Frameset. Последняя часть — это абсолютный путь (URI) к DTD-документу, который соответствует выбраному набору правил.

В данном случае, программист определяет, какой набор правил он будет использовать в разрабатываемом документе. Своё решение он выражает в виде записанного в начале HTML-файла DOCTYPE. Он,в свою очередь, будет использован браузером (User Agent) для того, чтобы выбрать соответствующий, правильный режим представления информации, более точную модель отображения данных. Можно сделать вывод, что выбор DTD является достаточно важной частью вёрстки, так как он накладывает отпечаток на последующие её этапы. Именно исходя из этого факта, можно говорить ещё и о том, что DTD является также и соглашением между Web-программистом и браузером, своеобразной гарантией правильного отображений документа.

Структура DTD

В дальнейшем мы будем рассматривать XHTML 1.0 Strict DTD.

Воспринимать структуру DTD можно достаточно просто, если помнить базовые типы данных, которые применяются в DTD. Все типы данных создаются конструкцией следующего вида:

<!тип_данных параметры>

Сейчас перечислим все возможные типы данных и остановимся на каждом из них подробно.

ENTITY

Данный тип данных используется для определения сущности, которая в дальнейшем будет подставляться в другие типы данных. Для того, чтобы лучше понять цель существования ENTITY, представьте себе константы в любом языке программирования и то, как они там используются.

Общий синтаксис ENTITY соответствует следующему выражению:

<!ENTITY % имя "содержимое">

Разберём части данного выражения.

  1. имя — это произвольный набор символов, который, в дальнейшем, может быть использован для подстановки данной сущности;
  2. содержимое — это набор данных, который будет подставлен заместо имени сущности.

Для того, чтобы подставить сущность в нужную часть DTD, используется конструкция следующего вида:

%имя;

Вот пример подобных конструкций:

<!ENTITY % Script "CDATA"> <!ENTITY % StyleSheet "CDATA">

Помимо такой записи сущностей, мы можем указать на то, что сущности находятся во внешнем файле, подключать который мы будем следующим образом:

<!ENTITY % имя PUBLIC "w3_путь" "имя_файла" > %имя;

В данной конструкции важную часть играет имя_файла, которое и указывает на тот файл, который нам следует подключить. Заметьте также после описания типа данных конструкцию %имя;, которая написана там для того, чтобы сразу подставить содержимое поключённого файла в сам DTD. Подобным образом, к примеру, описано подключение набора латинских символов:

<!ENTITY % HTMLlat1 PUBLIC "-//W3C//ENTITIES Latin 1 for XHTML//EN" "xhtml-lat1.ent"> %HTMLlat1;

ELEMENT

С помощью данного типа данных производится определение элемента (в нашей ситуации — HTML-элемента) и правил соблюдения иерархии для данного элемента (вложенность элементов).

Общий синтаксис ELEMENT соответствует следующему выражению:

<!ELEMENT имя_элемента правила_вложенности>

В этом случае имя_элемента будет использоваться для записи самого элемента непосредственно в HTML-документе:

<имя_элемента> </имя_элемента>

Теперь перейдём к правилам_вложенности: они могут быть записаны довольно большим количеством способов.

  1. EMPTY — элемент не может содержать закрывающего тега; в таком случае для закрытия тега используются конструкция />, а весь элемент принимает вид <имя_элемента />;
  2. (#PCDATA) или (#CDATA) — элемент может содержать только текстовое содержимое, то есть никаких вложенных тегов у него быть не может;
  3. (элемент1) — элемент может содержать только один указанный элемент элемент1;
  4. (элемент1|элемент2) — элемент может содержать либо элемент элемент1, либо элемент2, либо оба этих элемента;
  5. (элемент1, элемент2) — элемент должен содержать оба элемента: элемент1 и элемент2 (оба в единственном экземпляре);
  6. (…)+ — элемент должен содержать хотя бы один указанный элемент (или элементы);
  7. (…)* — элемент может содержать любое количество указанных элементов (или одного элемента), а может и не содержать их вообще;
  8. (…)? — элемент может содержать только один указанный элемент (или группу элементов).

Рассмотрим пример — объявление элемента html:

<!ELEMENT html (head, body)>

Данная конструкция говорит о том, что элемент html должен обязательно в себе содержать и элемент head, и элемент body. Ещё один пример, на этот раз связанный со списками:

<!ELEMENT ol (li)+>

Всё предельно понятно: элемент ol должен содержать хотя бы один вложенный элемент li.

Стоит заметить, что если вместо содержимого элемента встретится конструкция вида %имя;, то содержимое данной сущности (ENTITY) просто подставляется сюда и делается всё то же самое, что и без этой конструкции.

ATTLIST

Данный тип данных используется для определения атрибутов, которые может содержать указанный элемент.

Синтаксис определения списка атрибутов сводится к следующему виду:

<!ATTLIST имя_элемента имя_атрибута1 тип_содержимого_атрибута1 обязательность_атрибута1 имя_атрибута2 тип_содержимого_атрибута2 обязательность_атрибута2 имя_атрибута3 тип_содержимого_атрибута3 обязательность_атрибута3 ... >

Список атрибутов может быть бесконечно большим (то есть количество атрибутов для одного элемента неограничено). Чтобы было легче понять, как это работает, рассмотрим элемент с некоторыми атрибутами:

<имя_элемента имя_атрибута1="содержимое_атрибута1" имя_атрибута2="содержимое_атрибута2" имя_атрибута2="содержимое_атрибута3" >...</имя_элемента>

Теперь, думаю, примерно понятно, что обозначает каждая из колонок в объявлении атрибута. тип_содержимого_атрибута — это то, что может содержать в себе данный атрибут (набор символов, число, какое-то конкретное значение), а обязательность_атрибута говорит нам о том, обязателен ли данный атрибут для данного элемента. Рассмотрим значения обязательности атрибута более подробно:

  1. #IMPLIED — атрибут является не обязательным, но он может быть применён к данному элементу;
  2. #REQUIRED — атрибут должен обязательно быть записан для данного элемента;
  3. #FIXED 'значение' — содержимое атрибута должно быть установлено только в указанное значение.

Рассмотрим примеры. Перво-наперво, посмотрим на то, как записан элемент style и его атрибуты:

<!ELEMENT style (#PCDATA)> <!ATTLIST style %i18n; id ID #IMPLIED type %ContentType; #REQUIRED media %MediaDesc; #IMPLIED title %Text; #IMPLIED xml:space (preserve) #FIXED 'preserve' >

Видно, что элемент style не может содержать в себе вложенных тегов. Помимо этого, данный элемент должен обязательно содержать атрибут type, содержимое которого должно соответствовать сущности %ContentType;. Давайте найдём в файле DTD определение данной сущности:

<!ENTITY % ContentType "CDATA">

Это значит, что данный атрибут может содержать только символьные данные (текст). Обычно, в элементе style в данном атрибуте записано text/css. Если же посмотреть на атрибут xml:space, то он может содержать только значение preserve.

Попробуем создать весь HTML-элемент:

<style type="text/css" xml:space="preserve" media="all"> a { color: #444; } </style>

Данный элемент является валидным сам по себе. Но посмотрим же на другое использование данного элемента:

<style media="print"> #footer { display: none; } </style>

В этом случае элемент не является валидным, так как он не соответствует указанному набору правил, а именно не содержит в себе обязательный атрибут type.

Читаем DTD

Настало время проследить за тем, как можно использовать DTD в практических целях. К примеру, с помощью него мы можем узнать, как нам следует записывать те или иные элементы XHTML.

Давайте попробуем найти все блоковые элементы с помощью DTD:

<!ENTITY % Block "(%block; | form | %misc;)*">

Мы видим, что это определение включает в себя два других определения. Найдём же и их:

<!ENTITY % block "p | %heading; | div | %lists; | %blocktext; | fieldset | table">

Видим, что мы подобрались ещё ближе. Осталось найти ещё и объявленные здесь элементы — %heading;, %blocktext; и %lists; — и мы узнаем все блоковые элементы в HTML.

Давайте посмотрим на элемент h1:

<!ELEMENT h1 %Inline;> <!ATTLIST h1 %attrs; >

Видим, что данный элемент может содержать в себе только элементы, описанные в сущности %Inline;, поэтому нам стоит найти её и все используемые в ней другие сущности:

<!ENTITY % Inline "(#PCDATA | %inline; | %misc.inline;)*"> <!ENTITY % misc.inline "ins | del | script"> <!ENTITY % inline "a | %special; | %fontstyle; | %phrase; | %inline.forms;"> <!ENTITY % special "%special.pre; | object | img "> <!ENTITY % special.pre "br | span | bdo | map"> <!ENTITY % fontstyle "tt | i | b | big | small "> <!ENTITY % phrase "em | strong | dfn | code | q | samp | kbd | var | cite | abbr | acronym | sub | sup "> <!ENTITY % inline.forms "input | select | textarea | label | button">

Видим, что h1 может содержать все элементы текстового уровня, а также обычный набор символов (#PCDATA). Кроме того, отсюда сразу же можно сделать вывод о том, что данный элемент не может содержать в себе блоковые элементы: другие заголовочные элементы, параграфы, div, однако может содержать span, так как:

<!ENTITY % special.pre "br | span | bdo | map">

Здесь мы видим искомый наш элемент. Но вернёмся к нашему элементу h1. Видим в списке атрибутов лишь одну запись &attrs;, поэтому ищем запись вида <!ENTITY % attrs …:

<!ENTITY % attrs "%coreattrs; %i18n; %events;">

Вот оно: мы видим, что здесь используются другие группы элементов. Заметьте, что можно сразу же понять смысл определённых групп элементов: %events — это атрибуты для описания событий, %i18n — атрибуты для описания параметров интернационализации (языков, направления текста). Найдём, к примеру, последние:

<!ENTITY % i18n "lang %LanguageCode; #IMPLIED xml:lang %LanguageCode; #IMPLIED dir (ltr|rtl) #IMPLIED" >

Мы видим три определённых атрибута: lang, xml:lang, dir. Первые два содержат в себе описание языка, а последний может содержать только одно из двух значений: либо ltr, либо rtl.

Итоги

Многие считают, что DTD уже отживает своё. W3C продвигает новую XML Schema, которая в будуем заменит все декларации DTD. XML Schema является более органичной в том плане, что она сама написана с использованием XML и её легче обрабатывать, чем DTD. Что-ж, время покажет, но пока в интернете господствуют HTML 4, XHTML 1.0 рано хоронить DTD.

Да прибудут с вами спецификации, уважаемые читатели.

Мнения (10)

Все эти хорошие люди уже прокомментировали запись. Поделитесь собственным мнением, расскажите, что вы думаете о поставленной проблеме, задаче, озвученных мыслях.

  • Miscђka

    13 октября 2008 г.16:04

    <!DOCTYPE html> вроде бы автоматом дает привязку к HTML 5

  • Дин автор

    13 октября 2008 г.16:05

    Да, а что?

  • pepelsbey

    13 октября 2008 г.18:59

    То, что там нет явного указания на какой-то DTD.

    Просто «ну, вы все поняли о чём речь»…

  • Дин автор

    13 октября 2008 г.19:18

    @pepelsbey, так для HTML 5 ещё и DTD нет. К тому же, неизвестно, будет ли HTML 5 поставляться с DTD или же с XML Schema, или вообще с RELAX NG, кто знает.

    Текущий формат записи DOCTYPE для HTML 5 — не что иное, как «задел на будущее»: планируется, что в будущем все документы с Content-Type text/html будут считаться документами HTML 5. Мне пока слабо верится в быстрое наступление такого будущего.

  • Vii

    15 октября 2008 г.01:25
    В дальнейшем мы будем рассматривать XHTML 1.0 Strict DTD.

    У вас ссылка ведет на «http://www.w3.org/TR/xhtml1/DTD/xhtml1−frameset.dtd» (en-тире да еще и frameset вместо strict).

    А за статью спасибо, оч. интересно.

  • Дин автор

    15 октября 2008 г.07:59

    @Vii, спасибо, исправил.

  • SelenIT

    18 декабря 2008 г.23:24

    Судя по фразе из спецификации «XML DTDs cannot express all the conformance requirements of this specification», DTD для HTML5 точно не будет. Генри Сивонен сделал свой validator.nu на базе Relax NG (не без оговорок), а вообще HTML5 — не SGML-язык, там в принципе другой подход к проверке конформности (насколько мне известно). Обрубок доктайпа <!DOCTYPE html> оставили исключительно ради браузеров (чтобы они не падали в Quirks mode), самому языку доктайп вообще не нужен. XHTML5-вариант обходится без доктайпа — и, кстати, тоже адекватно воспринимается современными браузерами, понимающими application/xhtml+xml).

    А за статью (и вообще за весь цикл о валидации) огромное спасибо! Многим самоучкам (вроде меня) давно катастрофически не хватало такого доступного разложения по полочкам…

  • Дин автор

    22 декабря 2008 г.22:46

    @SelenIT, HTML 5 — соответствующее SGML приложение (как и все предыдущие версии), даже если правила проверки валидности и соответствия отличаются от таких же для предыдущих версий. Хотя я тут вспоминаю о том, что Relax NG используют и для валидации текущих версий HTML. SGML — это же не только определённый набор требований для валидации, а структурные элементы (и их описание), используемые в разметке. Исчезновение DTD говорит лишь о переходе на новую ступень валидации и только.

  • SelenIT

    23 декабря 2008 г.22:05

    @Дин, и все-таки:

    While the HTML form of HTML5 bears a close resemblance to SGML and XML, it is a separate language with its own parsing rules.

    Some earlier versions of HTML (in particular from HTML2 to HTML4) were based on SGML and used SGML parsing rules. However, few (if any) web browsers ever implemented true SGML parsing for HTML documents; the only user agents to strictly handle HTML as an SGML application have historically been validators. The resulting confusion — with validators claiming documents to have one representation while widely deployed Web browsers interoperably implemented a different representation — has wasted decades of productivity. This version of HTML thus returns to a non-SGML basis.

    Не спорю, это формальность, тем более, насколько я понимаю HTML5, в основе там вообще "in-memory DOM representation", а конкретный синтаксис сериализации вторичен. Но главное отличие HTML5 от предшественников -- все-таки правила парсинга, а не валидации, на мой взгляд…

  • Дин автор

    23 декабря 2008 г.22:29

    @SelenIT, разумеется.

    Валидация как раз напрямую зависит от парсинга (чтобы что-то усвоить, это что-то сначала нужно измельчить, переварить — всё как в человеческом организме). Разумеется, механизмы валидации могут быть основаны на совершенно других принципах обработки исходных данных, однако здравый смысл подсказывает, что при валидации документов используется такой же принцип обработки данных, как и при других процессах. Говоря русским языком, один из этапов валидации — парсинг, поэтому я и использую слово «валидация».

    Разработчики HTML 5 верно заметили, что текущая ситуация в отношениях UA и HTML складывается не лучшим образом и сделали правильное решение о возврате на негенерализованный базис, однако внешняя структура языка, его разметка, всё равно будет указывать на родство с корнями, как бы не хотели от этого уйти разработчики. Приложение SGML характеризует не только поднаготная — парсинг (и как этап валидации, и как самостоятельный процесс), но и формальная его сторона — «внешний вид языка», если угодно.

    Вот занятная цитата из руководства к ISO HTML, которое, к слову, появилось несколько раньше документов об HTML 5:

    In the frenzy of the growth, much of the discipline and good practice of the mature SGML world has been lost, and browser developers have added additional features to the markup language such as new tags and new semantics for tags. As a result, many documents have been created which can only be rendered faithfully on a limited number of browsers. Common web practice is to hide any syntactic problems detected by the browsers and thus the reader is not aware that a page being browsed is not always faithful to the original authored document.

    Разработчики правильно всё сделали, они молодцы.

Я тоже знаю!

Для обращения к человеку используйте символ @, после которого следует имя того, к кому обращаетесь (пробелы заменяются на знак подчёркивания). Если вам интересно, можете подписаться на комментарии по RSS или по эл. почте. Ведите себя достойно, вы же не роботы, правда?

Вы можете использовать следующие XHTML-элементы в разметке комментария: strong, em, span[class=crossline], a[href=uri], code[type=язык], blockquote, ul и ol. В качестве языка кода может быть указан, например, javascript или css.