Примечание: ниже перевод статьи 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-код, которому нельзя доверять, очистите сначала его на стороне сервера. Для этого существует огромное количество библиотек.Спасибо всем, кто ознакомился с заметкой. Буду рад ваших замечаниям на тему того как можно улучшить приведенный пример.