Мультівставка insertadjacenthtml і documentfragment

Як не дивно, між цими послідовностями є різниця. У більшості браузерів, другий варіант - швидше.

Чому ж? Іноді кажуть: «тому що браузер перемальовує кожен раз при додаванні елемента». Це не так. Справа зовсім не в перемальовуванні.

Браузер досить «розумний», щоб нічого не перемальовувати даремно. У більшості випадків процеси перемальовування і супутні обчислення будуть відкладені до закінчення роботи скрипта, і на той момент вже зовсім без різниці, в якій послідовності були змінені вузли.

Проте, при вставці вузла відбуваються різні внутрішні події та оновлення внутрішніх структур даних, приховані від наших очей.

Що саме відбувається - залежить від конкретної, внутрішньої браузерної реалізації 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 вставляти рядок. Всього чотири варіанти:

Наприклад, вставимо пропущені елементи списку перед

  • 5
  • :

    Єдиний недолік цього методу - він не працює в 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 + =. НЕ буде перезаписувати поточний зміст.

    Додайте до списку нижче елементи

  • 3
  • 4
  • 5
  • :

    Рядків в таблиці багато: може бути 20, 50, 100 ... Є й інші елементи в документі.

    Як зробити, щоб сортування працювала якомога швидше? А якщо в таблиці 10000 рядків (буває і таке)?

    P.S. Чи може тут допомогти DocumentFragment?

    Для сортування нам допоможе функція sort масиву.

    Загальна ідея лежить на поверхні: зробити масив з рядків і впорядкувати його. Тонкощі криються в деталях.

    У іфрейме нижче завантажений документ, що описує і реалізує різні алгоритми. Зверніть увагу: різниця в продуктивності може досягати декількох разів!

    P.S. Створювати DocumentFragment тут ні до чого. Можна витягнути з документа TBODY і мати справу з ним у відриві від DOM (алгоритм 4).

    P.P.S. Якщо потрібно зробити багато вузлів, то зазвичай innerHTML працює швидше, ніж видалення і вставка елементів через DOM-виклики. Тобто, згенерувати таблицю заново ефективніше.