Единая разметка: объясняя «@_jscript_version» и моделируя новые элементы HTML5

В прошлом месяце я опубликовал общее руководство по написанию кросс-браузерного кода. Особо я подчеркивал, что определение функциональности браузера (а не его самого) предпочтительнее для работы по определению различий между браузерами. Это происходит потому, что при определении функциональности происходит автоматическое приспособление к тому, на что способен данный браузер и, таким образом, элегантно учитываются новые выпуски продукта. Определение же браузера требует изучения уровня поддержки для каждой версии каждого браузера и нуждается в обновлении каждый раз, когда выпускается новый браузер. Сегодня я хочу перейти от последних обсуждений к примеру из реального мира. Начнем с фрагмента кода, похожего на следующий:

 // НЕ ИСПОЛЬЗУЙТЕ ЭТО
/*@cc_on
    @if( @_jscript_version < 9 )
        // Возможно моделирование новых элементов HTML5
        ...
    @end
@*/

Цель этого фрагмента – выявить и обойти отсутствующие возможности. В этом случае моделируются новые элементы HTML5, или более точно, используется CSS для переделки отображения новых элементов HTML5. В том виде, как написан, этот код не способен исполняться в режиме IE9 Compatibility View и приводит к тому, что новые элементы HTML5 остаются без моделирования. В режиме IE9 Standards Mode проблем не возникает, поскольку IE9 разрешает моделирование всех элементов по умолчанию.

Первой причиной, приводящей к данной ситуации, является использование условной компиляции JScript, подобной условным комментариям. Обе они являются формами определения типа браузера и, в общем случае, их надо избегать, особенно при позиционировании на последнюю версию браузера. Второй и более серьезной проблемой этого кода является попытка использования оператора @_jscript_version для определения режима документа на странице. Оператор @_jscript_version в действительности показывает, какая версия JScript используется браузером в целом. Во ВСЕХ режимах документа браузера IE9 этот оператор выдает «9». Во ВСЕХ режимах документа IE8 он выдает «5.8», а в IE7 – «5.7». Таким образом, это неверная информация для определения режима документа.

К счастью разработчики могут напрямую надежно определить режим документа. В IE8 был введен простой вызов функции DOM API, которая обеспечивает именно этой информацией: document . documentMode. Модификация исходного кода для использования этой функции приводит к следующему:

 // Уже лучше, но тоже не стоит использовать
// Избегайте, стараясь применять определение возможностей
if(document.documentMode < 9) {
    // Возможно моделирование новых элементов HTML5
    ...
}

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

Здесь показано, как это сделать:

 // ДЕЛАЙТЕ ТАК: Определение возможностей для моделирования неизвестных элементов
var elm = document.createElement("div");
elm.innerHTML = "<foo>test</foo>";
if(elm.childNodes.length !== 1) {
     // Возможно моделирование новых элементов HTML5
     var elms = [
          "abbr","article","aside","audio","canvas","command",
          "datalist","details","figcaption","figure","footer",
          "header","hgroup","mark","meter","nav","output",
          "progress","section","summary","time","video"
     ];
     for(var i = 0; i < elms.length; i++) {
          document.createElement(elms[i]);
     }
}

А теперь, как это работает.

Новые элементы HTML5 не могут быть смоделированы в версиях IE до IE9 по двум причинам. Во-первых, потому что новые элементы HTML5 трактуются как неизвестные элементы. Во-вторых, все ранние версии IE коллапсирует все неизвестные элементы во время синтаксического разбора. Коллапс просто означает, что все его дочерние элементы становятся дочерними элементами его родителя. Более того, сколлапсировавшие элементы приводят к отдельным вхождениям в DOM открывающих и закрывающих тэгов. Следующий код и результирующее представление в DOM иллюстрируют это утверждение:

 var elm = document.createElement("div");
elm.innerHTML = "<foo>test</foo>";

Результирующая DOM в IE8

 - <DIV>
    - test
    - </FOO>

Результирующая DOM в IE9

 - <div>
    - <foo>
        - test

Как вы видите, оператор elm.innerHTML = "<foo>test</foo>" в браузере, который коллапсирует неизвестные элементы, действительно приводит к двум дочерним элементам вместо одного. Такое поведение легко определить, используя регистрацию возможностей (т. е. проверяя, выполняется ли условие elm.childNodes.length !== 1).

Наконец необходимо понимать, что разрешение моделирования неизвестного элемента также просто, как и вызов document.createElement("unknownElementName")из сценария до того как любой элемент такого типа встретится анализатору HTML. Итак, разрешение моделирования новых элементов HTML5 включает в себя вызов document.createElement для каждого нового элемента, определенного спецификацией HTML5.

Этот подход является прекрасным примером преимуществ использования определения возможностей браузера, вместо выяснения его версии. Использование приведенного выше кода устраняет беспокойство о том, какая версия какого браузера поддерживает моделирование новых элементов HTML5. Так как код проверяет непосредственно само поведение, он автоматически обеспечивает соответствующий обходной маневр тогда, и только тогда, когда такой маневр действительно необходим.

Тони Росс (Tony Ross)

Руководитель команды разработчиков