Подтвердить что ты не робот

JavaScript: лучше ли использовать вызовы innerHTML или (много) createElement для добавления сложной структуры div?

Я ищу проблему, которая требует сложного блока divs, который будет создан один раз для каждого элемента в наборе из ~ 100 элементов.

Каждый отдельный элемент идентичен, за исключением содержимого, и они выглядят (в HTML) примерно так:

<div class="class0 class1 class3">
<div class="spacer"></div>
<div id="content">content</div>
<div class="spacer"></div>
<div id="content2">content2</div>
<div class="class4">content3</div>
<div class="spacer"></div>
<div id="footer">content3</div>
</div>

Я мог бы:

1) Создайте все элементы как innerHTML с конкатенацией строки, чтобы добавить контент.

2) Используйте createElement, setAttribute и appendChild для создания и добавления каждого div.

Вариант 1 получает немного меньший файл для загрузки, но вариант 2 кажется немного более быстрым для рендеринга.

Помимо производительности, есть ли веская причина идти по одному маршруту или другому? Любые проблемы с перекрестным браузером/производительность gremlins, на которые я должен проверить?

... или я должен попробовать шаблон и клон?

Большое спасибо.

4b9b3361

Ответ 1

Ни. Используйте библиотеку, например jQuery, Prototype, Dojo или mooTools, потому что оба этих метода чреваты неприятностями:

Авторы основных библиотек javascript потратили много времени и имеют целые системы отслеживания ошибок, чтобы убедиться, что когда вы вызываете их инструменты изменения DOM, они действительно работают.

Если вы пишете библиотеку, чтобы конкурировать с вышеуказанными инструментами (и удачи вам, если вы есть), тогда я бы выбрал метод, основанный на производительности, а innerHTML всегда выигрывал в прошлом, и поскольку innerHTML является нативным методом, безопасная ставка будет оставаться самой быстрой.

Ответ 2

Зависит от того, что "лучше" для вас.

Производительность

С точки зрения производительности, createElement + appendChild выигрывает LOT. Взгляните на этот jsPerf, который я создал, когда сравниваю оба и результаты, попавшие в лицо.

innerHTML: ~120 ops/sec
createElement+appendChild: ~145000 ops/sec

(на моем Mac с Chrome 21)

Кроме того, innerHTML запускает переполнение страницы.

В Ubuntu с Chrome 39 тесты получают похожие результаты

innerHTML: 120000 ops/sec
createElement: 124000 ops/sec

возможно, какая-то оптимизация. На Ubuntu с QtWebkit на основе браузера Arora (wkhtml также QtWebkit) результаты

innerHTML: 71000 ops/sec
createElement: 282000 ops/sec

кажется, что createElement быстрее в среднем

Mantainability

С точки зрения удобства, я считаю, что строковые шаблоны вам очень помогают. Я использую Handlebars (что мне нравится) или Tim (для проекта, требующего наименьших отпечатков). Когда вы "скомпилируете" (подготовьте) свой шаблон и готовы к его добавлению в DOM, вы используете innerHTML, чтобы добавить строку шаблона в DOM. В фокусе, который я делаю, чтобы избежать reflow, это createElement для обертки и в этом элементе-оболочке, поставьте шаблон с помощью innerHTML. Я все еще ищу хороший способ избежать innerHTML вообще.

Совместимость

Здесь вам не о чем беспокоиться, оба метода полностью поддерживаются широким спектром браузеров (в отличие от altCognito). Вы можете проверить графики совместимости для createElement и appendChild.

Ответ 3

altCognito делает хороший момент - использование библиотеки - путь. Но если бы это делалось вручную, я бы использовал опцию №2 - создать элементы с помощью методов DOM. Они немного уродливы, но вы можете создать элемент factory, который скрывает уродство. Конкатенация строк HTML также уродлива, но чаще возникает проблема безопасности, особенно с XSS.

Я бы определенно не добавлял новые узлы по отдельности. Я бы использовал DOM DocumentFragment. Добавление узлов в documentFragment происходит намного быстрее, чем вставка их в живую страницу. Когда вы закончите создание своего фрагмента, он просто будет вставлен сразу.

Джон Ресиг объясняет это намного лучше, чем я мог, но в основном вы просто говорите:

var frag = document.createDocumentFragment();
frag.appendChild(myFirstNewElement);
frag.appendChild(mySecondNewElement);
...etc.
document.getElementById('insert_here').appendChild(frag);

Ответ 4

Лично я использую innerHTML, потому что это то, к чему я привык, и для чего-то вроде этого, методы W3C добавляют в код много помех.

Можно ли просто сократить число div, есть ли какие-либо причины, по которым вы используете элементы spacer, вместо того, чтобы просто редактировать поля в div div?

Ответ 5

Поскольку вы упомянули шаблон и клон, вас может заинтересовать этот вопрос:

Другим вариантом является использование обертки DOM, например DOMBuilder:

DOMBuilder.apply(window);
DIV({"class": "class0 class1 class3"},
  DIV({"class": "spacer"}),
  DIV({id: "content"}, "content"),
  DIV({"class": "spacer"}),
  DIV({id: "content2"}, "content2"),
  DIV({"class": "class4"}, "content3"),
  DIV({"class": "spacer"}),
  DIV({id: "footer"}, "content3")
);

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

Ответ 6

Ни. Используйте cloneNode.

var div = document.createElement('div');
var millionDivs = [div];
while (millionDivs.length < 1e6) millionDivs.push(div.cloneNode())

Ответ 7

Я не думаю, что там можно выбирать между ними. В прежние дни (IE6, FF1.5), innerHTML был быстрее (benchmark), но теперь не получается чтобы быть заметной разницей в большинстве случаев.

В соответствии с mozilla dev. docs существует несколько ситуаций, когда поведение innerHTML варьируется между браузерами (особенно внутри таблиц), поэтому createElement даст вам больше согласованности - но innerHTML обычно меньше вводить.

Ответ 8

Как я знаю, самый быстрый способ - избегать редактирования DOM, если это возможно. Это означает, что лучше создать большую строку, а затем поместить ее в innerHTML. Но есть замечание для этого: не делайте слишком много операций над большими строками, быстрее использовать массив строк, а затем присоединить их все.

Ответ 9

Для такой сложной задачи я обычно использую методы innerHTML, потому что они: a) легче читать и модифицировать по коду; b) более удобно использовать в циклах. Как говорится в главном сообщении, они, к сожалению, терпят неудачу в IE (6,7,8) на элементах <table>, <thead>, <tbody>, <tr>, <tfoot>, <select>, <pre>.

Ответ 10

1) Создайте все элементы как innerHTML с конкатенацией строк, чтобы добавить контент.

2) Используйте createElement, setAttribute и appendChild для создания и добавления каждого div.

3) компромисс. Создайте все элементы за один раз в виде innerHTML (что позволяет избежать небольшого количества манипулирования списком дочерних элементов), а затем записывайте содержимое, которое изменяется на каждом элементе, используя доступ к данным/атрибуту (избегая всех этих неприятных ошибок при использовании HTML-escape содержание). например. что-то вроде:

var html= (
    '<div class="item">'+
        '<div class="title">x</div>'+
        '<div class="description">x</div>'+
    '</div>'
);
var items= [
    {'id': 1, 'title': 'Potato', 'description': 'A potato'},
    {'id': 2, 'title': 'Kartoffel', 'description': 'German potato'},
    // ... 100 other items ...
];

function multiplyString(s, n) {
    return new Array(n+1).join(s);
}

var parent= document.getElementById('itemcontainer');
parent.innerHTML= multiplyString(html, items.length);
for (var i= 0; i<items.length; i++) {
    var item= items[i];
    var node= parent.childNodes[i];

    node.id= 'item'+item.id;
    node.childNodes[0].firstChild.data= item.title;
    node.childNodes[1].firstChild.data= item.description;
}

Можно также комбинировать с подсказкой Neall о DocumentFragments.