Сегодня утром вернулся к выводу RSS-ленты на нашем сайте. И обнаружил, что
С недавних пор мы решили верстать сайты в XHTML. Затея не нова, скажете вы, — сейчас каждый уважающий себя верстальщик все делает по стандартам и о валидности не забывает. Однако, как мы можем узнать из статьи Ивана Сагалаева «XHTML, говорите?», не все так просто, и многие разработчики не подозревают, что верстают вовсе не в режиме XHTML, а в старом добром HTML–режиме. Дело в HTTP-заголовке Content-type, и вся свистопляска начинается только тогда, когда вы вместо привычного
Классическим примером разницы в движках HTML и XHTML было отсутствие у XHTML-элементов свойства
Вот какие исключения бывают (на тот случай, если кто-то, как и я, гуглить начинает с текста ошибки целиком):
Firefox 2: An invalid or illegal string was specified
Firefox 3: Component returned failure code: 0x80004003 (NS_ERROR_INVALID_POINTER) [nsIDOMNSHTMLElement.innerHTML]
Safari 3: NO_MODIFICATION_ALLOWED_ERR: DOM Exception 7
Opera 9 умеет парсить HTML сама. За что нижайший ей поклон.
Первое решение, которое пришло мне на ум, это поправить разметку простенькими регулярками.
После этого чать кода заработала. Потом из любопытства поглядел на список сущностей в HTML. А потом попался фрагмент кода с перемешанными тегами. Писать парсер перехотелось. Помучавшись в раздумиях, решил попробовать распарсить HTML на сервере, а потом выдать его как HTML. Идея мне понравилась, по натуре я серверный программист и раньше воспринимал браузер только как дисплей. Потом, правда, одумался.
Одуматься удалось и на этот раз. Зачем, спрашиввается, мне парсить HTML на сервере, если у меня в руках самый совершенный парсер HTML — собственно браузер. Не найдя рационального ответа на этот вопрос, решил парсить кривой HTML на клиенте. Вот только куда тиранический XML-режим сослал этот самый парсер — не понятно.
Отступлю немного и отвечу на вопрос, ответ на который вас, возможно, интересует. Почему у меня HTML весь такой кривой и неправильный. Ответ прост как шланг — на нашем блоге мы решили парсить сырую RSS-выдачу. А получаем мы очень вольно составленный HTML.
Так вот. Где же cпрятан парсер HTML, когда у нас все кругом в XHTML? Сегодня получить парсер в лисе, скриптом невозможно. Про вебкит и оперу по запросу "new HTMLDocument()" тоже ничего найти не удалось (искал, правда, спустя рукава — любимчик-то мой уже облажался).
Промучавшись полдня с «крутыми» технологиями, решил попробовать самую простую — спрятать iframe и в него загрузить html-файл. Как мы и рассчитываем
Как это выгладит в коде? Как всегда было в HTML:
Родные, милые черты...
Осталось извлечь полученное дерево и доставить его на место назначения.
В будущих версиях браузеров, возможно, надо будет адаптировать ноды
Теперь нам можно завернуть полученные знания в модуль и распространять, наслаждаясь славой и признанием.
Итог. HTML парсится и выводится в Firefox 2, 3 и Safari 3. Opera 9 все умеет сама. IE и Safari 2 не умеют XHTML совсем, но все же умеют клиентский XSL через xml-stylesheet. Получается, мостик между HTML и XML перебросили.
innerHTML меня не спасает. Что же делать? Решение есть, но сначала хорошенько опишу проблему.С недавних пор мы решили верстать сайты в XHTML. Затея не нова, скажете вы, — сейчас каждый уважающий себя верстальщик все делает по стандартам и о валидности не забывает. Однако, как мы можем узнать из статьи Ивана Сагалаева «XHTML, говорите?», не все так просто, и многие разработчики не подозревают, что верстают вовсе не в режиме XHTML, а в старом добром HTML–режиме. Дело в HTTP-заголовке Content-type, и вся свистопляска начинается только тогда, когда вы вместо привычного
text/html дадите браузеру нюхнуть application/xhtml+xml. Или text/xml на худой конец. Браузер сразу обнаружит, что вы, наконец, одумались и выбрали самый современный отформатированный формат документов и сопутствующие ему самые стандартные стандарты. Однако прежней свою страницу вы не увидите. Делов-то, сейчас слеши в конце <br> и <img> поставим, и все. А не все. Когда вы начнете программировать, предварительно исправив разметку, указав пространство имен и разобравшись с кодировкой, вас настигнут бонусные неприятности с передовым форматом. Самое время вернуться к теме поста.Классическим примером разницы в движках HTML и XHTML было отсутствие у XHTML-элементов свойства
innerHTML. То есть загрузить что-то аморфное аяксом и вывести в div стало нельзя. Свойство присваивается, JavaScript ведь этого не запрещает, но реакции никакой нет. Народ стал мучаться с интерфейсами DOMParser и DOMSerializer, чтобы эмулировать любимое свойство. И, честно говоря, некоторый успех был. Позже браузеры научились и сами имитировать innerHTML в режиме XHTML (Ьщяшддф вероятнее всего тем же способом, Opera тоже умеет) и сейчас можно не опасаясь писать нечто вроде $('content').innerHTML = '<h1>Сильно <i>лень</i> городить createElement() и appendChild()</h1>'. Хотя свойство и называется innerHTML на самом деле HTML ему скормить нельзя. Это скорее innerXHTML. То есть такое дело не пройдет: mailDiv.innerHTML = '<p>забудем закрыть "p", поставим неразрывный пробел, а еще картинку вставим <img alt="постринке">'. И что произойдет? В обычном режиме браузер напряжет свои HTML-извилины и догадается о том, что вы имели ввиду. В новом мире XML умен должен быть не браузер, а мы. А сам браузер, как ему и положено, откажется парсить такую гадость и выбросит исключение.Вот какие исключения бывают (на тот случай, если кто-то, как и я, гуглить начинает с текста ошибки целиком):
Firefox 2: An invalid or illegal string was specified
Firefox 3: Component returned failure code: 0x80004003 (NS_ERROR_INVALID_POINTER) [nsIDOMNSHTMLElement.innerHTML]
Safari 3: NO_MODIFICATION_ALLOWED_ERR: DOM Exception 7
Opera 9 умеет парсить HTML сама. За что нижайший ей поклон.
Первое решение, которое пришло мне на ум, это поправить разметку простенькими регулярками.
markup = markup.replace(/<(img|br|hr|link|input)( .+?)\/?>/g, '<$1$2/>')
markup = markup.replace(/( )/g, ' ')После этого чать кода заработала. Потом из любопытства поглядел на список сущностей в HTML. А потом попался фрагмент кода с перемешанными тегами. Писать парсер перехотелось. Помучавшись в раздумиях, решил попробовать распарсить HTML на сервере, а потом выдать его как HTML. Идея мне понравилась, по натуре я серверный программист и раньше воспринимал браузер только как дисплей. Потом, правда, одумался.
Одуматься удалось и на этот раз. Зачем, спрашиввается, мне парсить HTML на сервере, если у меня в руках самый совершенный парсер HTML — собственно браузер. Не найдя рационального ответа на этот вопрос, решил парсить кривой HTML на клиенте. Вот только куда тиранический XML-режим сослал этот самый парсер — не понятно.
Отступлю немного и отвечу на вопрос, ответ на который вас, возможно, интересует. Почему у меня HTML весь такой кривой и неправильный. Ответ прост как шланг — на нашем блоге мы решили парсить сырую RSS-выдачу. А получаем мы очень вольно составленный HTML.
Так вот. Где же cпрятан парсер HTML, когда у нас все кругом в XHTML? Сегодня получить парсер в лисе, скриптом невозможно. Про вебкит и оперу по запросу "new HTMLDocument()" тоже ничего найти не удалось (искал, правда, спустя рукава — любимчик-то мой уже облажался).
XMLParser, как видно из названия, парсит только XML (правда это может быть и XUL, и MathML, и SVG и…). Создать новый HTML-документ с помощью document.implementation.createDocument(… ) можно, но будет он XHTML. Конструкция (new DOMParser()).parseFromString("...", "text/html") не сработает, так как DOMParser не поддерживает text/html. Прямо так и пишет: not implemented.Промучавшись полдня с «крутыми» технологиями, решил попробовать самую простую — спрятать iframe и в него загрузить html-файл. Как мы и рассчитываем
window.onload срабатывает когда все загрутся, в том числе и фрейм. Добавлять фрейм можно динамически, но тогда onload может произойти раньше, чем iframe будет готов. Так как я успел и не забыл, браузеры меня вознаградили. Наградой явился объект document, который умеет парсить HTML. Ура!Как это выгладит в коде? Как всегда было в HTML:
var html = $('htmliframe').contentDocument.firstChi ld
html.innerHTML = '<div>' + markup + '</div>'Родные, милые черты...
Осталось извлечь полученное дерево и доставить его на место назначения.
// почистим ноду назначения
while (destination.firstChild)
destination.removeChild(destination.firs tChild)
// склонируем HTML-дерево (в нашем случае див с детьми)
clone = html.firstChild.cloneNode(true)
// вставим див куда надо
content.appendChild(clone)В будущих версиях браузеров, возможно, надо будет адаптировать ноды
clone = document.adoptNode(clone), но пока они на adoptNode ругаются.Теперь нам можно завернуть полученные знания в модуль и распространять, наслаждаясь славой и признанием.
Итог. HTML парсится и выводится в Firefox 2, 3 и Safari 3. Opera 9 все умеет сама. IE и Safari 2 не умеют XHTML совсем, но все же умеют клиентский XSL через xml-stylesheet. Получается, мостик между HTML и XML перебросили.
