Единая разметка: объясняя «@_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)
Руководитель команды разработчиков