На практике, каковы преимущества использования createElement над innerHTML? Я спрашиваю, потому что я убежден, что использование innerHTML более эффективно с точки зрения производительности и удобочитаемости кода/ремонтопригодности, но мои товарищи по команде остановились на использовании createElement в качестве подхода к кодированию. Я просто хочу понять, как createElement может быть более эффективным.
Преимущества createElement над innerHTML?
Ответ 1
Существует несколько преимуществ использования createElement
вместо изменения innerHTML
(в отличие от того, чтобы просто выбросить то, что уже есть и заменить его), кроме безопасности, как уже упоминалось Пекка:
Сохраняет существующие ссылки на элементы DOM при добавлении элементов
Когда вы добавляете (или иным образом изменяете) innerHTML
, все узлы DOM внутри этого элемента должны быть повторно обработаны и воссозданы. Если вы сохранили какие-либо ссылки на узлы, они будут бесполезны, потому что они больше не отображаются.
Сохраняет обработчики событий, прикрепленные к любым элементам 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>
Ответ 2
Хотя 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
});
Ответ 3
Пользователь bobince очень и очень хорошо описывает ряд минусов в своей критике jQuery.
... Кроме того, вы можете сделать div, сказав $ ('' + message + '') вместо того, чтобы возиться с document.createElement('div') и текстовыми узлами. Ура! Только... держись. Вы не избежали этого HTML и, возможно, только что создали дыру в безопасности межсайтовых сценариев, только на этот раз на стороне клиента. И после того, как вы потратили так много времени на очистку своего PHP, чтобы использовать htmlspecialchars и на стороне сервера. Какой позор. Ну что ж, никто не заботится о правильности или безопасности, не так ли?
JQuery не совсем виноват в этом. В конце концов, свойство innerHTML существует уже много лет и уже оказалось более популярным, чем DOM. Но библиотека, безусловно, поощряет такой стиль кодирования.
Что касается производительности: InnerHTML определенно будет работать медленнее, потому что он должен быть проанализирован и внутренне преобразован в элементы DOM (возможно, с использованием метода createElement
).
InnerHTML работает быстрее во всех браузерах в соответствии с тестом quirksmode, предоставленным @Pointy.
Что касается читабельности и простоты использования, вы обнаружите, что я выбираю innerHTML
вместо createElement
в любой день недели в большинстве проектов. Но, как вы можете видеть, есть много точек, говорящих за createElement
.
Ответ 4
Вы должны использовать createElement, если хотите сохранить ссылки в своем коде. InnerHTML может иногда создавать ошибку, которую трудно обнаружить.
Код HTML:
<p id="parent">sample <span id='test'>text</span> about anything</p>
Код JS:
var test = document.getElementById("test");
test.style.color = "red"; //1 - it works
document.getElementById("parent").innerHTML += "whatever";
test.style.color = "green"; //2 - oooops
1) вы можете изменить цвет
2) вы больше не можете менять цвет или что-то еще, потому что в строке выше вы добавили что-то в InnerHTML, и все заново создано, и у вас есть доступ к тому, что больше не существует. Чтобы изменить его, вам нужно снова getElementById.
Вы должны помнить, что это также влияет на любые события. Вам необходимо повторно применить события.
InnerHTML отлично, потому что быстрее и удобнее читать, но вы должны быть осторожны и использовать его с осторожностью. Если вы знаете, что делаете, все будет в порядке.
Ответ 5
Шаблонные литералы (Шаблонные строки) - это еще один вариант.
const container = document.getElementById("container");
const item_value = "some Value";
const item = '<div>${item_value}</div>'
container.innerHTML = item;