На практике, каковы преимущества использования createElement по innerHTML? Я спрашиваю, потому что я убежден, что использование innerHTML более эффективно с точки зрения производительности и удобочитаемости/пригодности для обслуживания кода, но мои товарищи по команде обосновались на использовании createElement как подход кодирования. Я просто хочу понять, как createElement может быть более эффективным.
Использование createElement
вместо модификации innerHTML
дает несколько преимуществ (в отличие от того, чтобы просто выбросить то, что уже есть, и заменить его) помимо безопасности, как уже упоминал Пекка:
Когда вы добавляете (или иным образом изменяете) innerHTML
, все узлы DOM внутри этого элемента должны быть повторно проанализированы и воссозданы. Если вы сохранили какие-либо ссылки на узлы, они будут практически бесполезны, потому что больше не будут отображаться.
Это действительно частный случай (хотя и общий) последнего. Установка innerHTML
не будет автоматически повторно присоединять обработчики событий к новым элементам, которые он создает, поэтому вам придется отслеживать их самостоятельно и добавлять их вручную. В некоторых случаях делегирование событий может устранить эту проблему.
Если вы делаете много дополнений, вы определенно не хотите продолжать сбрасывать innerHTML
, потому что, хотя и быстрее для простых изменений, многократно повторный синтаксический анализ и создание элементов будет медленнее. Чтобы обойти это, нужно создать HTML-код в виде строки и установить innerHTML
один раз, когда вы закончите. В зависимости от ситуации манипуляции со строками могут быть медленнее, чем просто создание элементов и их добавление.
Кроме того, код обработки строк может быть более сложным (особенно, если вы хотите, чтобы он был безопасным).
Вот функция, которую я иногда использую, которая делает более удобным использование createElement
.
function isArray(a) {
return Object.prototype.toString.call(a) === "[object Array]";
}
function make(desc) {
if (!isArray(desc)) {
return make.call(this, Array.prototype.slice.call(arguments));
}
var name = desc[0];
var attributes = desc[1];
var el = document.createElement(name);
var start = 1;
if (typeof attributes === "object" && attributes !== null && !isArray(attributes)) {
for (var attr in attributes) {
el[attr] = attributes[attr];
}
start = 2;
}
for (var i = start; i < desc.length; i++) {
if (isArray(desc[i])) {
el.appendChild(make(desc[i]));
}
else {
el.appendChild(document.createTextNode(desc[i]));
}
}
return el;
}
Если вы назовете это так:
make(["p", "Here is a ", ["a", { href:"http://www.google.com/" }, "link"], "."]);
вы получите эквивалент этого HTML:
<p>Here is a <a href="http://www.google.com/">link</a>.</p>
Хотя innerHTML
может быть быстрее, я не согласен с тем, что он лучше с точки зрения удобочитаемости или обслуживания. Сложить все в одну строку может быть короче, но более короткий код не всегда удобнее в сопровождении.
Конкатенация строк просто не масштабируется, когда необходимо создавать динамические элементы DOM, так как открытие и закрытие плюсов и кавычек становится трудно отслеживать. Рассмотрим эти примеры:
Результирующий элемент - это div с двумя внутренними промежутками, содержимое которых является динамическим. Одно из имен класса (воин) внутри первого промежутка также является динамическим.
<div>
<span class="person warrior">John Doe</span>
<span class="time">30th May, 2010</span>
</div>
Предположим, что следующие переменные уже определены:
var personClass = 'warrior';
var personName = 'John Doe';
var date = '30th May, 2010';
Используя только innerHTML и объединяя все в одну строку, мы получаем:
someElement.innerHTML = "<div><span class='person " + personClass + "'>" + personName + "</span><span class='time'>" + date + "</span></div>";
Вышеупомянутый беспорядок можно устранить с помощью замены строк, чтобы избежать открытия и закрытия строк каждый раз . Даже для простой замены текста я предпочитаю использовать replace
вместо конкатенации строк.
Это простая функция, которая берет объект ключей и замещающих значений и заменяет их в строке. Предполагается, что ключи имеют префикс $
, чтобы обозначить, что это особое значение. Он не выполняет никаких экранирований и не обрабатывает крайние случаи, когда $
появляется в значении замены и т. Д.
function replaceAll(string, map) {
for(key in map) {
string = string.replace("$" + key, map[key]);
}
return string;
}
var string = '<div><span class="person $type">$name</span><span class="time">$date</span></div>';
var html = replaceAll(string, {
type: personClass,
name: personName,
date: date
});
someElement.innerHTML = html;
Это можно улучшить, разделив атрибуты, текст и т. Д. При построении объекта, чтобы сделать объект более программным. контроль конструкции элемента. Например, с помощью MooTools мы можем передавать свойства объекта в виде карты. Это, безусловно, более удобно в обслуживании, и я бы сказал, что он более удобочитаемый. jQuery 1.4 использует аналогичный синтаксис для передачи карты для инициализации объектов DOM.
var div = new Element('div');
var person = new Element('span', {
'class': 'person ' + personClass,
'text': personName
});
var when = new Element('span', {
'class': 'time',
'text': date
});
div.adopt([person, when]);
Я бы не назвал приведенный ниже чистый подход DOM более читабельным, чем приведенный выше, но он определенно более удобен в обслуживании, потому что нам не нужно отслеживать открывающие / закрывающие кавычки и многочисленные знаки плюса.
var div = document.createElement('div');
var person = document.createElement('span');
person.className = 'person ' + personClass;
person.appendChild(document.createTextNode(personName));
var when = document.createElement('span');
when.className = 'date';
when.appendChild(document.createTextNode(date));
div.appendChild(person);
div.appendChild(when);
Наиболее удобочитаемая версия, скорее всего, будет результатом использования какого-нибудь шаблона JavaScript .
<div id="personTemplate">
<span class="person <%= type %>"><%= name %></span>
<span class="time"><%= date %></span>
</div>
var div = $("#personTemplate").create({
name: personName,
type: personClass,
date: date
});
Пользователь bobince очень и очень хорошо излагает ряд минусов в своей критике jQuery.
... Плюс, вы можете сделать div, сказав $(''+message+''), вместо того, чтобы возиться с document.createElement('div') и текстовыми узлами. Ура! Только... подождите. Вы не экранировали этот HTML и, вероятно, только что создали дыру в безопасности межсайтового скриптинга, только на этот раз на стороне клиента. И после того, как вы потратили столько времени на очистку PHP для использования htmlspecialchars на стороне сервера. Какой позор. А, ладно, никто ведь не заботится о правильности и безопасности, не так ли?
jQuery не полностью виноват в этом. В конце концов, свойство innerHTML существует уже много лет и уже оказалось более популярным, чем DOM. Но библиотека определенно поощряет такой стиль кодирования.
Что касается производительности: InnerHTML определенно будет медленнее, потому что его нужно разобрать и внутренне преобразовать в элементы DOM (возможно, используя метод createElement
).
InnerHTML быстрее во всех браузерах, согласно quirksmode benchmark, предоставленному @Pointy.
Что касается читабельности и простоты использования, то в большинстве проектов я в любой день недели выбираю innerHTML
вместо createElement
. Но, как вы видите, есть много аргументов в пользу createElement
.