Примечание: ниже перевод статьи Julien Lecomte «The Problem With innerHTML», в которой автор рассматривает проблемы при использовании метода innerHTML в современных браузерах и предлагает ряд советов, как ее можно избежать. Мои комментарии далее курсивом
Свойство
Есть и еще несколько более мелких недостатков, которые тоже стоит упомянуть:
Лично меня больше волнуют вопросы безопасности и использовании памяти, связанные с использованием свойства
Douglas Crockford написал функцию
Удаление тега
Сейчас давайте попробуем совместить обе эти техники в одной функции
Вуаля! Сообщите мне, пожалуйста, если я упустил что-то в реализации данной функции или нужно усовершенствовать регулярное выражение.
UPD: Существует огромное количество путей для вставки вредоносного кода на веб-страницу. Функция
Спасибо всем, кто ознакомился с заметкой. Буду рад ваших замечаниям на тему того как можно улучшить приведенный пример.
Свойство
innerHTML крайне популярно среди веб-разработчиков в силу своей простоты и удобства, поскольку оно совершено элементарно позволяет заменить HTML-содержание у конкретного тега. Можно также воспользоваться DOM Level 2 API (removeChild, createElement, appendChild), но использование innerHTML гораздо более простой и эффективный способ для модификации DOM-дерева. Однако, есть ряд проблем при использовании innerHTML, которых следует избегать:- Неправильная обработка свойства
innerHTMLможет привести к атакам, связанным со script-инъекциями (XSS) в Internet Explorer, когда HTML-строка содержит вызов<script>, помеченного как отложенный:<script defer>...</script> - Выставление свойства
innerHTMLуничтожит все текущие вложенные HTML-элементы со всеми обработчиками событий, что потенциально может вызвать утечки памяти в некоторых браузерах.
Есть и еще несколько более мелких недостатков, которые тоже стоит упомянуть:
- Нельзя получить ссылку на только что созданные элементы, вам приходится добавлять код для получения ссылки на них вручную (используя DOM API).
- Вы не можете выставить
innerHTMLдля всех HTML-элементов во всех браузерах (к примеру, Internet Explorer не позволяет выставитьinnerHTMLдля строки таблицы (tr)).
Лично меня больше волнуют вопросы безопасности и использовании памяти, связанные с использованием свойства
innerHTML. Однако, описанная проблема далеко не нова, и уже некоторые светлые головы уделили ей внимание и предложили методы, для ее решения.Douglas Crockford написал функцию
purge, которая удаляет некоторые циклические ссылки (circular references), вызванные добавлением обработчиков событий к HTML-элементам, позволяя сборщику мусора (garbage collector) полностью освободить всю память, с ними связанную.Удаление тега
<script> из HTML-строки не так просто, как кажется на первый взгляд. Регулярное выражение, используемое для этой цели, должно быть достаточно сложным, хотя я сам не знаю, охватывает ли оно все возможные случаи. Ниже вариант, который я лично используя в работе:/<script[^>]*>[\S\s]*?<\/script[^>]*>/ig
Сейчас давайте попробуем совместить обе эти техники в одной функции
setInnerHTML (UPD: спасибо всем, кто добавил свои комментарии: я поправил все ошибки/дыры, на которые вы указали. А также решил включить функцию setInnerHTML в YAHOO.util.Dom)
YAHOO.util.Dom.setInnerHTML = function (el, html) {
el = YAHOO.util.Dom.get(el);
if (!el || typeof html !== 'string') {
return null;
}
// удаляем циклические ссылки
(function (o) {
var a = o.attributes, i, l, n, c;
if (a) {
l = a.length;
for (i = 0; i < l; i += 1) {
n = a[i].name;
if (typeof o[n] === 'function') {
o[n] = null;
}
}
}
a = o.childNodes;
if (a) {
l = a.length;
for (i = 0; i < l; i += 1) {
c = o.childNodes[i];
// Удаляем дочерние узлы
arguments.callee(c);
// Удаляем все обработчики событий,
// добавленные к элементу через YUI addListener
YAHOO.util.Event.purgeElement(c);
}
}
})(el);
// Удаляем все скрипты из HTML-строки и выставляем свойство innerHTML
el.innerHTML = html.replace(/<script[^>]*>[\S\s]*?<\/script[^>]*>/ig, "");
// Возвращаем ссылку на первый дочерний узел
return el.firstChild;
};Вуаля! Сообщите мне, пожалуйста, если я упустил что-то в реализации данной функции или нужно усовершенствовать регулярное выражение.
UPD: Существует огромное количество путей для вставки вредоносного кода на веб-страницу. Функция
setInnerHTML всего лишь нормализирует поведение тега <script> при исполнении по всех браузерах класса А (A-grade browsers). Если вы собираетесь вставить HTML-код, которому нельзя доверять, очистите сначала его на стороне сервера. Для этого существует огромное количество библиотек.Спасибо всем, кто ознакомился с заметкой. Буду рад ваших замечаниям на тему того как можно улучшить приведенный пример.