Давеча я
рассказывал, как Opera обламывает jQuery-разработчика при попытке использовать XHTML своей некорректной
работой с innerHTML. А теперь расскажу, как можно попытаться это дело исправить.
Итак, пишем для Opera такую функцию:
function appendXHTML(node, xhtml){
var doc = (new DOMParser()).parseFromString(
''
+ xhtml + '', "application/xhtml+xml"
).documentElement;
var frag=document.createDocumentFragment();
var elem=doc.firstChild;
while(elem){
frag.appendChild(elem.cloneNode(true));
elem=elem.nextSibling;
};
node.appendChild(frag);
};
Она запишет в документ правильный XHTML-фрагмент. Вопрос в том, как её задействовать. Ныне Opera поддерживает изначально Mozilla’вские методы __defineGetter__ и __defineSetter__, позволяющие определить свои обработчики для чтения и присвоения некоторого свойства объекта. К сожалению, переопределить с их помощью уже имеющееся свойство innerHTML мне не удалось (может, чего-то не понял?), получилось только определить новое свойство:
if("HTMLElement" in window){
HTMLElement.prototype.__defineSetter__(
"innerXHTML",
function(val){
if("opera" in window && window.opera){
while(this.firstChild)
this.removeChild(this.firstChild);
appendXHTML(this, val);
}
else
this.innerHTML=val;
}
);
}
Памятуя, что разработка идёт с использованием jQuery, функцию-сеттер лучше переписать так:
function(val){
if($.browser.opera){
$(this).empty();
appendXHTML(this, val);
}
else
this.innerHTML=val;
}
Теперь можно попытаться позаменять в собственном коде jQuery все обращения к innerHTML на обращения к innerXHTML (да, автозамена - это не очень хорошо, но что ж поделаешь?). Только тогда нам ещё понадобится функция-геттер (на случай, когда свойство будет использоваться для чтения). Она тривиальна (пишем её рядышком, внутрь проверки существования HTMLElement):
HTMLElement.prototype.__defineGetter__(
"innerXHTML",
function(){
return this.innerHTML;
}
);
Недовольным останется только Internet Explorer и на сей раз даже вполне оправданно, ибо он реализует (пусть и не в полном объёме) не проприетарные придумки, а метод defineProperty из стандарта ECMAScript 5. Специально для него пишем:
if("defineProperty" in Object)
Object.defineProperty(
Element.prototype,
"innerXHTML",
{
get: function(){
return this.innerHTML;
},
set: function(val){
this.innerHTML=val;
}
}
);
Разумеется, после этого IE продолжит писать XHTML код по-своему, как и раньше, ну, да он всё равно XHTML так пока и не понимает и, что ценно, в отличие от Opera не притворяется перед jQuery, что это не так.
Вот и всё. На примерах работает в IE 8 (в IE 7 не работает, что на нынешний момент можно ещё, наверное, считать серьёзным ограничением; и ещё, очень важно - никакого Quirks Mode, корректный DOCTYPE должен быть указан!), Firefox 2.0 и 3.5, Opera 9.6 (в Opera 9.0 не работает), а также в Chrome 3.0 и Safari 4.0. На большом проекте метод пока не испытывал.