Як не дивно, між цими послідовностями є різниця. У більшості браузерів, другий варіант - швидше.
Чому ж? Іноді кажуть: «тому що браузер перемальовує кожен раз при додаванні елемента». Це не так. Справа зовсім не в перемальовуванні.
Браузер досить «розумний», щоб нічого не перемальовувати даремно. У більшості випадків процеси перемальовування і супутні обчислення будуть відкладені до закінчення роботи скрипта, і на той момент вже зовсім без різниці, в якій послідовності були змінені вузли.
Проте, при вставці вузла відбуваються різні внутрішні події та оновлення внутрішніх структур даних, приховані від наших очей.
Що саме відбувається - залежить від конкретної, внутрішньої браузерної реалізації DOM, але це забирає час. Звичайно, браузери розвиваються і намагаються звести зайві дії до мінімуму.
Щоб легко перевірити поточний стан справ - ось два бенчмарка.
Обидва вони створюють таблицю 20x20, наповнюючи TBODY елементами TR / TD.
При цьому перший вставляє все в документ тут же, другий - затримує вставку TBODY в документ до кінця процесу.
Натисніть, щоб запустити.
Код для тестів знаходиться в файлі insert-bench.js.
Продовжимо працювати зі вставкою вузлів.
Розглянемо випадок, коли в документі вже є великий список UL. І тут знадобилося терміново додати ще 20 елементів LI.
Як це зробити?
Якщо нові елементи прийшли в вигляді рядка, то можна спробувати додати їх так:
Але операцію ul.innerHTML + = "." Можна по-іншому переписати як ul.innerHTML = ul.innerHTML + ".". Інакше кажучи, вона не додає, а замінює весь вміст списку на доповнену рядок. Це і недобре з точки зору продуктивності, але і будуть побічні ефекти. Зокрема, всі зовнішні ресурси (картинки) всередині перезаписуваного innerHTML будуть завантажені заново. Якщо в якихось змінних були посилання на елементи списку - вони стануть невірні, так як вміст повністю замінюється. Загалом, так краще не робити.
А якщо потрібно вставити в середину списку? Тут innerHTML взагалі не допоможе.
Можна, звичайно, вставити рядок в тимчасовий DOM-елемент і перенести звідти елементи, але є і набагато кращий варіант: метод insertAdjacentHTML!
Метод insertAdjacentHTML дозволяє вставляти довільний HTML в будь-яке місце документа, в тому числі і між вузлами!
Він підтримується всіма браузерами, крім Firefox менше версії 8, ну а там його можна емулювати.
html Рядок HTML, яку потрібно вставити
where: Куди по відношенню до elem вставляти рядок. Всього чотири варіанти:
Наприклад, вставимо пропущені елементи списку перед
Єдиний недолік цього методу - він не працює в Firefox до версії 8. Але його можна легко додати, використовуючи поліфілл insertAdjacentHTML для Firefox.
У цього методу є «близнюки-брати», які підтримуються всюди, крім Firefox, але в нього вони додаються тим же поліфіллом:
- elem.insertAdjacentElement (where, newElem) - вставляє в довільне місце не рядок HTML, а елемент newElem.
- elem.insertAdjacentText (where, text) - створює текстовий вузол з рядка text і вставляє його в зазначене місце щодо elem.
Синтаксис цих методів, за винятком останнього параметра, повністю збігається з insertAdjacentHTML. Разом вони утворюють «універсальний швейцарський ніж» для вставки чого завгодно куди завгодно.
Важливо для старих браузерів
Оптимізація, про яку тут йдеться, важлива в першу чергу для старих браузерів, включаючи IE9-. В сучасних браузерах ефект від неї, як правило, невеликий, а іноді може бути і негативним.
До цього ми говорили про вставці рядка в DOM. А що робити в разі, коли треба в існуючий UL вставити багато DOM-елементів?
Можна вставляти їх один за іншим, викликом insertBefore / appendChild. але при цьому вийде багато операцій з великим живим документом.
Вставити пачку вузлів одноразово допоможе DocumentFragment. Це особливий крос-браузерні DOM-об'єкт, який схожий на звичайний DOM-вузол, але їм не є.
Синтаксис для його створення:
У DocumentFragment немає звичайних властивостей DOM-вузлів, таких як innerHTML. tagName і т.п. Це не вузол.
Його «Фішка» полягає в тому, що коли DocumentFragment вставляється в DOM - то він зникає, а замість нього вставляються його діти. Ця властивість є унікальною особливістю DocumentFragment.
Наприклад, якщо додати в нього багато LI. і потім викликати ul.appendChild (fragment). то фрагмент розчиниться, і в DOM вставити саме LI. причому в тому ж порядку, в якому були у фрагменті.
В сучасних браузерах ефект від такої оптимізації може бути різним, а на невеликих документах іноді і негативним.
Зрозуміти поточний стан речей ви можете, запустивши наступний невеликий бенчмарк.
Порівняно недавно в стандарті з'явилися методи, які дозволяють вставити що завгодно і куди завгодно.
- node.append (. nodes) - вставляє nodes в кінець node,
- node.prepend (. nodes) - вставляє nodes в початок node,
- node.after (. nodes) - вставляє nodes після вузла node,
- node.before (. nodes) - вставляє nodes перед вузлом node,
- node.replaceWith (. nodes) - вставляє nodes замість node.
Ці методи нічого не повертають.
У всіх цих методах nodes - DOM-вузли або рядки, в будь-якому поєднанні і кількості. Причому рядки вставляються саме як текстові вузли, на відміну від insertAdjacentHTML.
Приклад (з поліфіллом):
Маніпуляції, що змінюють структуру DOM (вставка, видалення елементів), як правило, швидше за окремим маленьким вузлом, ніж з великим DOM, який знаходиться в документі.
Конкретна різниця залежить від внутрішньої реалізації DOM в браузері.
Сімейство методів для вставки HTML / елемента / тексту в довільне місце документа:
- elem.insertAdjacentHTML (where, html)
- elem.insertAdjacentElement (where, node)
- elem.insertAdjacentText (where, text)
Два останніх методу не підтримуються в Firefox, на момент написання тексту, але є невеликий поліфілл insertAdjacentFF.js. який додає їх. Звичайно, він потрібен лише для Firefox.
DocumentFragment дозволяє мінімізувати кількість вставок в великою живою DOM. Ця оптимізація особливо ефективна в старих браузерах, в нових ефект від неї менше або навпаки негативний.
Елементи спочатку вставляються в нього, а потім - він вставляється в DOM. При вставці DocumentFragment «розчиняється», і замість нього вставляються містяться в ньому вузли.
DocumentFragment. на відміну від insertAdjacent *. працює з колекцією DOM-вузлів.
Сучасні методи, працюють з будь-якою кількістю вузлів і тексту, бажаний поліфілл:
- append / prepend - вставка в кінець / початок.
- before / after - вставка перед / після.
- replaceWith - заміна.
Напишіть код для вставки тексту html в кінець списку ul з використанням методу insertAdjacentHTML. Така вставка, на відміну від присвоєння innerHTML + =. НЕ буде перезаписувати поточний зміст.
Додайте до списку нижче елементи
Рядків в таблиці багато: може бути 20, 50, 100 ... Є й інші елементи в документі.
Як зробити, щоб сортування працювала якомога швидше? А якщо в таблиці 10000 рядків (буває і таке)?
P.S. Чи може тут допомогти DocumentFragment?
Для сортування нам допоможе функція sort масиву.
Загальна ідея лежить на поверхні: зробити масив з рядків і впорядкувати його. Тонкощі криються в деталях.
У іфрейме нижче завантажений документ, що описує і реалізує різні алгоритми. Зверніть увагу: різниця в продуктивності може досягати декількох разів!
P.S. Створювати DocumentFragment тут ні до чого. Можна витягнути з документа TBODY і мати справу з ним у відриві від DOM (алгоритм 4).
P.P.S. Якщо потрібно зробити багато вузлів, то зазвичай innerHTML працює швидше, ніж видалення і вставка елементів через DOM-виклики. Тобто, згенерувати таблицю заново ефективніше.