Что лучше: добавление новых элементов с помощью функций DOM или добавление строк с помощью HTML-тегов?

Я видел несколько разных способов добавления элементов в DOM. Наиболее распространенными кажутся, например, либо

document.getElementById('foo').innerHTML ='<p>Here is a brand new paragraph!</p>';

or

newElement = document.createElement('p');
elementText = document.createTextNode('Here is a brand new parahraph!');
newElement.appendChild(elementText);
document.getElementById('foo').appendChild(newElement);

но я не уверен в преимуществах выполнения любого из них. Есть ли эмпирическое правило относительно того, когда одно должно быть сделано над другим, или одно из них просто неправильно?


person Chris Sobolewski    schedule 11.12.2011    source источник
comment
Спасибо, исправлено. Слишком привык писать на jQuery.   -  person Chris Sobolewski    schedule 11.12.2011
comment
innerHTML('<p>Here is a brand new paragraph!</p>'); должно быть innerHTML = '<p>Here is a brand new paragraph!</p>'; я думаю?   -  person Merlyn Morgan-Graham    schedule 11.12.2011
comment
На различные ответы здесь: innerHTML определенно не быстрее. Это ложное срабатывание старых браузеров (IE). document.createElement теперь сильно оптимизирован для многих браузеров. См.: jsperf.com/dom-table-generation.   -  person    schedule 11.12.2011
comment
Также представляет интерес шпаргалка OWASP по коду DOM (который осуждает innerHTML): owasp.org/index. php/DOM_based_XSS_Prevention_Cheat_Sheet   -  person    schedule 11.12.2011
comment
@MattMcDonald: хотя страница quirksmode устарела, ссылка на тест, который создает тысячи пустых таблиц в тесном цикле, не доказывает вашу точку зрения. У innerHTML есть накладные расходы на прохождение синтаксического анализатора, поэтому он должен быть медленнее, чем разумная реализация DOM1 для тривиальных модификаций. С другой стороны, при построении сложной модели DOM использование createElement и подобных ему требует гораздо большего взаимодействия JS‹->C++, чем настройка innerHTML. К сожалению, эти вызовы по-прежнему не имеют нулевых накладных расходов. Было бы интересно увидеть реальные результаты на реальных рабочих нагрузках.   -  person Nickolay    schedule 12.12.2011
comment
@MattMcDonald - я согласен с Николаем; это не очень показательный пример. См. это: jsperf.com/innerhtml-vs-w3c-dom   -  person Wayne    schedule 12.12.2011
comment
@lwburk игнорируя тот факт, что предложенный вами пример никогда не следует использовать в производственной среде, вы продаете W3C DOM без покрытия. См. мою версию: jsperf.com/innerhtml-vs-w3c-dom/2   -  person    schedule 12.12.2011
comment
@MattMcDonald - Абсолютно. Спасибо, что вынесли на обсуждение фрагменты документов.   -  person Wayne    schedule 12.12.2011


Ответы (6)


Некоторые примечания:

  • Использование innerHTML быстрее в IE, но медленнее в chrome + firefox. Вот один тест, показывающий это с постоянно меняющимся набором <div>s + <p>s; вот тест, показывающий это для постоянного простого <table>.

  • С другой стороны, методы DOM являются традиционным стандартом — innerHTML стандартизированы в HTML5 — и позволяют вам сохранять ссылки на вновь созданные элементы, чтобы вы могли изменить их позже.

  • Поскольку innerHTML является быстрым (достаточно), лаконичным и простым в использовании, возникает соблазн использовать его в любой ситуации. Но имейте в виду, что использование innerHTML отсоединяет все существующие узлы DOM от документа. Вот пример, который вы можете протестировать на этой странице.

    Во-первых, давайте создадим функцию, которая позволит нам проверить, находится ли узел на странице:

    function contains(parent, descendant) {
        return Boolean(parent.compareDocumentPosition(descendant) & 16);
    }
    

    Это вернет true, если parent содержит descendant. Протестируйте это так:

    var p = document.getElementById("portalLink")
    console.log(contains(document, p)); // true
    document.body.innerHTML += "<p>It's clobberin' time!</p>";
    console.log(contains(document, p)); // false
    p = document.getElementById("portalLink")
    console.log(contains(document, p)); // true
    

    Это напечатает:

    true
    false
    true
    

    Может показаться, что использование innerHTML не должно было повлиять на нашу ссылку на элемент portalLink, но это так. Его необходимо снова получить для правильного использования.

person Wayne    schedule 11.12.2011
comment
Отличный ответ, но, пожалуйста, перефразируйте первое утверждение (быстрее innerHTML) или укажите источник. Мне не ясно, что на самом деле быстрее, и есть ссылка на тест, который показывает, что это не всегда быстрее в современных браузерах. См. комментарий Мэтта Макдональда к вопросу. - person Nickolay; 12.12.2011
comment
@Nickolay - Смотрите мое редактирование. innerHTML кажется значительно быстрее на более сложном вводе. - person Wayne; 12.12.2011
comment
Спасибо, что нашли время создать достаточно сложный пример, подтверждающий мои подозрения! - person Nickolay; 12.12.2011
comment
@Nickolay Связанный тест содержал ошибки (из-за чего версия innerHTML заменяла содержимое циклов предыдущего теста, а версия DOM создавала постоянно растущий документ), из-за чего DOM выглядел медленнее, чем он есть. После исправления версия innerHTML работает медленнее в FF и Chrome, но все равно быстрее в IE11. - person Eamon Nerbonne; 13.12.2013
comment
Но имейте в виду, что использование innerHTML отсоединяет все существующие узлы DOM от документа. Это утверждение невероятно обманчиво. Утверждение верно только в том случае, если вы используете innerHTML для объекта document.body. Если вы измените innerHTML элемента div где-нибудь в документе, будут отсоединены только узлы DOM в этом элементе div. - person user3163495; 15.09.2016

Есть ряд отличий:

  1. innerHTML был стандартизирован W3C только для HTML 5; несмотря на то, что он уже некоторое время является де-факто стандартом для всех популярных браузеров, технически в HTML 4 это расширение поставщика, которое никогда не застанет врасплох разработчиков, придерживающихся стандартов. с использованием. С другой стороны, это гораздо удобнее и поддерживается практически всеми браузерами.
  2. innerHTML заменяет текущее содержимое элемента (не позволяет вам изменить его). Но опять же, вы выигрываете в удобстве, если не возражаете против этого ограничения.
  3. innerHTML было измерено намного быстрее (правда, в этом тесте участвуют более старые версии браузеров, которые в настоящее время не используется).
  4. innerHTML может представлять угрозу безопасности (XSS), если для него задано введенное пользователем значение, которое не было должным образом закодировано (например, el.innerHTML = '<script>...').

Исходя из вышеизложенного, можно сделать практический вывод:

  • Если вы не возражаете против того факта, что innerHTML немного ограничивает (только полная замена поддерева DOM с корнем в целевом элементе) и вы не рискуете получить уязвимость из-за внедрения пользовательского контента, используйте его. В противном случае используйте DOM.
person Jon    schedule 11.12.2011
comment
Просто общий комментарий, который адресован не вам, а пункту номер 1. Меня смешит каждый раз, когда я слышу, как разработчик говорит, что он никогда не будет нарушать стандарты, а во втором предложении ему нужно научиться мыслить нестандартно. .. - person Itay Moav -Malimovka; 11.12.2011
comment
@ItayMoav: ИМХО, тот, кто никогда не нарушит стандарты, никогда не работал с реальными клиентами и дедлайнами. Но я также считаю, что немного опасно связывать нестандартное мышление с чем бы то ни было, это похоже на рабочий менталитет, который мы видим снова и снова. - person Jon; 11.12.2011
comment
здесь должен помочь опыт. Чтобы помочь нам выбрать правильный инструмент. Кроме того, такой менталитет гарантирует, что у меня будет много работы и еды на тарелке :-D - person Itay Moav -Malimovka; 11.12.2011
comment
innerHTML ;) использовать заглавные буквы HTML - person Trevor; 11.12.2011

Согласно этим эталонным данным, вы получите гораздо более быстрые результаты с innerHTML, чем с созданием элементов DOM. Это особенно заметно при использовании старых версий IE.

person Brigand    schedule 11.12.2011

Хотя это старый поток, одна вещь, которая не упоминается, это то, что хотя innerHTML может быть быстрее, следует соблюдать осторожность. Использование innerHTML отобразит каждый дочерний элемент измененного элемента, как старый, так и новый. Таким образом, одно присваивание innerHTML выполняется быстрее (немного), чем создание/добавление DOM, но несколько innerHTML определенно будут медленнее.

Например:

for(let i=0; i < 10; i++)
   document.body.innerHTML+='<div>some text</div>';

будет почти в 5 раз медленнее, чем

let html = '';
for(let i=0; i < 10; i++)
   html += '<div>some text</div>';

document.body.innerHTML = html;

Поскольку назначение innerHTML позволяет браузеру создавать/добавлять элементы изначально, второй метод приводит к созданию/добавлению 10 элементов, в то время как первый метод приводит к созданию/добавлению 55 элементов (и 45 уничтожению): 1 элемент создается в первом цикле -итерация, 2 элемента создаются на второй итерации цикла (исходный уничтожается), 3 элемента создаются на третьей итерации цикла (предыдущие 2 уничтожаются) и так далее.

Если вы используете innerHTML для скорости, вы должны сначала создать всю строку html, прежде чем выполнять назначение innerHTML, например, создавать новые контейнеры/элементы DOM. innerHTML, с другой стороны, теряет производительность при добавлении любого контейнера с существующими дочерними узлами, особенно с большим количеством дочерних узлов.

person codechimp    schedule 22.03.2019

Первый из них прямолинеен, легче читается, требует меньше кода и может быть быстрее.
Второй дает вам гораздо больший контроль над создаваемым элементом, т. е. значительно упрощает изменение нового элемента с помощью JS (например, присоединение событий, или просто используйте его в своем коде).
Второй способ предназначен для «чистых», которым нравится «чистый» код (не быстрый и грязный).
Я говорю, используйте оба, посмотрите, что вам больше подходит, и используйте Это.

person Itay Moav -Malimovka    schedule 11.12.2011

Я всегда предпочитаю удобочитаемость, если только разница в производительности не является экстремальной. В единичном случае это, вероятно, будет незначительной разницей.

В единичном случае, подобном этому, установка свойства innerHTML будет проще для чтения.

Но если вы много программно генерируете контент в JavaScript, вариант DOM чище и проще для чтения и понимания.

Пример:

Сравните этот код innerHTML:

http://jsfiddle.net/P8m3K/1/

// Takes input of a value between 1 and 26, inclusive,
// and converts it to the appropriate character
function alphaToChar(alpha)
{
    return String.fromCharCode('a'.charCodeAt() + alpha - 1);
}

var content = "<ul>";
for(i = 0; i < 10; ++i)
{
    content += "<li>";
    for(j = 1; j <= 26; ++j)
    {
        content += "<a href=\"" + alphaToChar(j) + ".html\">"
            + alphaToChar(j)
            + "</a>";
    }
    content += "</li>";
}
document.getElementById("foo").innerHTML = content;

К этому коду DOM:

http://jsfiddle.net/q6GB8/1/

// Takes input of a value between 1 and 26, inclusive,
// and converts it to the appropriate character
function alphaToChar(alpha)
{
    return String.fromCharCode('a'.charCodeAt() + alpha - 1);
}

var list = document.createElement("ul");
for(i = 0; i < 10; ++i)
{
    var item = document.createElement("li");
    for(j = 1; j <= 26; ++j)
    {
        var link = document.createElement("a");
        link.setAttribute("href", alphaToChar(j) + ".html");
        link.innerText = alphaToChar(j);
        item.appendChild(link);
    }
    list.appendChild(item);
}
document.getElementById("foo").appendChild(list);

На этом уровне они начинают становиться очень похожими по длине.

Но код DOM будет легче поддерживать, и у вас немного меньше шансов сделать опечатку или ошибку, которую трудно диагностировать, например, пропуск закрывающего тега. Либо ваши элементы будут в вашем документе, либо их не будет.

  • С более сложными сценариями (такими как построение древовидных меню) вы, вероятно, выйдете вперед с кодом DOM.
  • В сценариях, в которых вам нужно добавить несколько типов содержимого вместе, чтобы создать документ с более разнородным содержимым, это становится беспроигрышным вариантом. Вам не нужно гарантировать, что вы вызываете свой дочерний код добавления перед вызовом родительского кода добавления.
  • В сценариях, где добавляют, удаляют или изменяют существующий статический контент, обычно выигрывает DOM.

Если вы начнете делать сложные модификации DOM (одна из последних вещей, которые я упомянул), вам определенно захочется проверить библиотеку, построенную на модификациях DOM, как jQuery.

person Merlyn Morgan-Graham    schedule 11.12.2011
comment
Я знаю из вашего комментария к OP, что вы использовали jQuery. Я просто упомянул это для людей, которые найдут ваш вопрос в будущем :) - person Merlyn Morgan-Graham; 11.12.2011