# Мультивставка: insertAdjacentHTML и DocumentFragment Обычные методы вставки работают с одним узлом. Но есть и способы вставлять множество узлов одновременно. [cut] ## Оптимизация вставки в документ Рассмотрим задачу: сгенерировать список `UL/LI`. Есть две возможных последовательности:
  1. Сначала вставить `UL` в документ, а потом добавить к нему `LI`: ```js var ul = document.createElement('ul'); document.body.appendChild(ul); // сначала в документ for(...) ul.appendChild(li); // потом узлы ```
  2. Полностью создать список "вне DOM", а потом -- вставить в документ: ```js var ul = document.createElement('ul'); for(...) ul.appendChild(li); // сначала вставить узлы document.body.appendChild(ul); // затем в документ ```
Как ни странно, между этими последовательностями есть разница. В большинстве браузеров, второй вариант -- быстрее. Почему же? Иногда говорят: "потому что браузер перерисовывает каждый раз при добавлении элемента". Это не так. Дело вовсе не в перерисовке. Браузер достаточно "умён", чтобы ничего не перерисовывать понапрасну. В большинстве случаев процессы перерисовки и сопутствующие вычисления будут отложены до окончания работы скрипта, и на тот момент уже совершенно без разницы, в какой последовательности были изменены узлы. **Тем не менее, при вставке узла происходят разные внутренние события и обновления внутренних структур данных, скрытые от наших глаз.** Что именно происходит -- зависит от конкретной, внутренней браузерной реализации DOM, но это отнимает время. Конечно, браузеры развиваются и стараются свести лишние действия к минимуму. [online] ### Бенчмарк [#insert-bench-tbody] Чтобы легко проверить текущее состояние дел -- вот два бенчмарка. Оба они создают таблицу 20x20, наполняя TBODY элементами TR/TD. При этом первый вставляет все в документ тут же, второй -- задерживает вставку TBODY в документ до конца процесса. Кликните, чтобы запустить.
Код для тестов находится в файле [insert-bench.js](insert-bench.js). [/online] ## Добавление множества узлов Продолжим работать со вставкой узлов. Рассмотрим случай, когда в документе *уже есть* большой список `UL`. И тут понадобилось срочно добавить еще 20 элементов `LI`. Как это сделать? Если новые элементы пришли в виде строки, то можно попробовать добавить их так: ```js ul.innerHTML += "
  • 1
  • 2
  • ..."; ``` Но операция `+=` с `innerHTML` не работает с DOM. Она не прибавляет, а заменяет всё содержимое списка на дополненную строку. Это не только медленно, но все внешние ресурсы (картинки) будут загружены заново. Так лучше не делать. А если нужно вставить в середину списка? Здесь `innerHTML` вообще не поможет. Можно, конечно, вставить строку во временный DOM-элемент и перенести оттуда элементы, но есть и гораздо лучший вариант: метод `insertAdjacentHTML`! ## insertAdjacent* Метод [insertAdjacentHTML](https://developer.mozilla.org/en/DOM/element.insertAdjacentHTML) позволяет вставлять произвольный HTML в любое место документа, в том числе *и между узлами*! Он поддерживается всеми браузерами, кроме Firefox меньше версии 8, ну а там его можно эмулировать. Синтаксис: ```js elem.insertAdjacentHTML(where, html); ```
    `html`
    Строка HTML, которую нужно вставить
    `where`
    Куда по отношению к `elem` вставлять строку. Всего четыре варианта:
    1. `beforeBegin` -- перед `elem`.
    2. `afterBegin` -- внутрь `elem`, в самое начало.
    3. `beforeEnd` -- внутрь `elem`, в конец.
    4. `afterEnd` -- после `elem`.
    Например, вставим пропущенные элементы списка *перед* `
  • 5
  • `: ```html ``` Единственный недостаток этого метода -- он не работает в Firefox до версии 8. Но его можно легко добавить, используя [полифилл insertAdjacentHTML для Firefox](insertAdjacentHTML.js). У этого метода есть "близнецы-братья", которые поддерживаются везде, кроме Firefox, но в него они добавляются тем же полифиллом: Синтаксис этих методов, за исключением последнего параметра, полностью совпадает с `insertAdjacentHTML`. Вместе они образуют "универсальный швейцарский нож" для вставки чего угодно куда угодно. ## DocumentFragment [warn header="Важно для старых браузеров"] Оптимизация, о которой здесь идёт речь, важна в первую очередь для старых браузеров, включая IE9-. В современных браузерах эффект от нее, как правило, небольшой, а иногда может быть и отрицательным. [/warn] До этого мы говорили о вставке строки в DOM. А что делать в случае, когда надо в существующий `UL` вставить много *DOM-элементов*? Можно вставлять их один за другим, вызовом `insertBefore/appendChild`, но при этом получится много операций с большим живым документом. **Вставить пачку узлов единовременно поможет `DocumentFragment`. Это особенный *кросс-браузерный* DOM-объект, который похож на обычный DOM-узел, но им не является.** Синтаксис для его создания: ```js var fragment = document.createDocumentFragment(); ``` В него можно добавлять другие узлы. ```js fragment.appendChild(node); ``` Его можно клонировать: ```js fragment.cloneNode(true); // клонирование с подэлементами ``` **У `DocumentFragment` нет обычных свойств DOM-узлов, таких как `innerHTML`, `tagName` и т.п. Это не узел.** Его "Фишка" заключается в том, что когда `DocumentFragment` вставляется в DOM -- то он исчезает, а вместо него вставляются его дети. Это свойство является уникальной особенностью `DocumentFragment`. Например, если добавить в него много `LI`, и потом вызвать `ul.appendChild(fragment)`, то фрагмент растворится, и в DOM вставятся именно `LI`, причём в том же порядке, в котором были во фрагменте. Псевдокод: ```js // хотим вставить в список UL много LI // делаем вспомогательный DocumentFragment var fragment = document.createDocumentFragment(); for (цикл по li) { fragment.appendChild(list[i]); // вставить каждый LI в DocumentFragment } ul.appendChild(fragment); // вместо фрагмента вставятся элементы списка ``` В современных браузерах эффект от такой оптимизации может быть различным, а на небольших документах иногда и отрицательным. Понять текущее положение вещей вы можете, запустив следующий [edit src="benchmark"]небольшой бенчмарк[/edit]. ## append/prepend, before/after, replaceWith Сравнительно недавно в [стандарте](https://dom.spec.whatwg.org/) появились методы, которые позволяют вставить что угодно и куда угодно. Синтаксис: Эти методы ничего не возвращают. Во всех этих методах `nodes` -- DOM-узлы или строки, в любом сочетании и количестве. Причём строки вставляются именно как текстовые узлы, в отличие от `insertAdjacentHTML`. Пример (с полифиллом): ```html ``` ## Итого [head] [/head]