- Мультивставка: insertAdjacentHTML и DocumentFragment
- Оптимизация вставки в документ
- Бенчмарк
- Добавление множества узлов
- insertAdjacent*
- DocumentFragment
- append/prepend, before/after, replaceWith
- Итого
- Задачи
- Отсортировать таблицу
- Element.insertAdjacentHTML()
- Синтаксис
- Параметры
- Наглядное отображение параметра position
- Пример
- Примечания
- Соображения безопасности
- Спецификации
- Совместимость с браузерами
- Смотрите также
- Found a content problem with this page?
- MDN
- Support
- Our communities
- Developers
- Element.insertAdjacentElement()
- Синтаксис
- Параметры
- Возвращаемое значение
- Исключения
- Наглядное отображение параметра position
- Примеры
- Спецификация
- Совместимость с браузерами
- Смотрите также
- Found a content problem with this page?
- MDN
- Support
- Our communities
- Developers
Мультивставка: insertAdjacentHTML и DocumentFragment
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/modifying-document.
Обычные методы вставки работают с одним узлом. Но есть и способы вставлять множество узлов одновременно.
Оптимизация вставки в документ
Рассмотрим задачу: сгенерировать список UL/LI .
Есть две возможных последовательности:
- Сначала вставить UL в документ, а потом добавить к нему LI :
var ul = document.createElement('ul'); document.body.appendChild(ul); // сначала в документ for (. ) ul.appendChild(li); // потом узлы
var ul = document.createElement('ul'); for(. ) ul.appendChild(li); // сначала вставить узлы document.body.appendChild(ul); // затем в документ
Как ни странно, между этими последовательностями есть разница. В большинстве браузеров, второй вариант – быстрее.
Почему же? Иногда говорят: «потому что браузер перерисовывает каждый раз при добавлении элемента». Это не так. Дело вовсе не в перерисовке.
Браузер достаточно «умён», чтобы ничего не перерисовывать понапрасну. В большинстве случаев процессы перерисовки и сопутствующие вычисления будут отложены до окончания работы скрипта, и на тот момент уже совершенно без разницы, в какой последовательности были изменены узлы.
Тем не менее, при вставке узла происходят разные внутренние события и обновления внутренних структур данных, скрытые от наших глаз.
Что именно происходит – зависит от конкретной, внутренней браузерной реализации DOM, но это отнимает время. Конечно, браузеры развиваются и стараются свести лишние действия к минимуму.
Бенчмарк
Чтобы легко проверить текущее состояние дел – вот два бенчмарка.
Оба они создают таблицу 20×20, наполняя TBODY элементами TR/TD .
При этом первый вставляет все в документ тут же, второй – задерживает вставку TBODY в документ до конца процесса.
Код для тестов находится в файле insert-bench.js.
Добавление множества узлов
Продолжим работать со вставкой узлов.
Рассмотрим случай, когда в документе уже есть большой список UL . И тут понадобилось срочно добавить ещё 20 элементов LI .
Если новые элементы пришли в виде строки, то можно попробовать добавить их так:
Но операцию ul.innerHTML += «. » можно по-другому переписать как ul.innerHTML = ul.innerHTML + «. » . Иначе говоря, она не прибавляет, а заменяет всё содержимое списка на дополненную строку. Это и нехорошо с точки зрения производительности, но и будут побочные эффекты. В частности, все внешние ресурсы (картинки) внутри перезаписываемого innerHTML будут загружены заново. Если в каких-то переменных были ссылки на элементы списка – они станут неверны, так как содержимое полностью заменяется. В общем, так лучше не делать.
А если нужно вставить в середину списка? Здесь innerHTML вообще не поможет.
Можно, конечно, вставить строку во временный DOM-элемент и перенести оттуда элементы, но есть и гораздо лучший вариант: метод insertAdjacentHTML !
insertAdjacent*
Метод insertAdjacentHTML позволяет вставлять произвольный HTML в любое место документа, в том числе и между узлами!
elem.insertAdjacentHTML(where, html);
Строка HTML, которую нужно вставить
Куда по отношению к elem вставлять строку. Всего четыре варианта:
- beforeBegin – перед elem .
- afterBegin – внутрь elem , в самое начало.
- beforeEnd – внутрь elem , в конец.
- afterEnd – после elem .
У этого метода есть «близнецы-братья»:
- elem.insertAdjacentElement(where, newElem) – вставляет в произвольное место не строку HTML, а элемент newElem .
- elem.insertAdjacentText(where, text) – создаёт текстовый узел из строки text и вставляет его в указанное место относительно elem .
Синтаксис этих методов, за исключением последнего параметра, полностью совпадает с insertAdjacentHTML . Вместе они образуют «универсальный швейцарский нож» для вставки чего угодно куда угодно.
DocumentFragment
Оптимизация, о которой здесь идёт речь, важна в первую очередь для старых браузеров, включая IE9-. В современных браузерах эффект от неё, как правило, небольшой, а иногда может быть и отрицательным.
До этого мы говорили о вставке строки в DOM. А что делать в случае, когда надо в существующий UL вставить много DOM-элементов?
Можно вставлять их один за другим, вызовом insertBefore/appendChild , но при этом получится много операций с большим живым документом.
Вставить пачку узлов единовременно поможет DocumentFragment . Это особенный кросс-браузерный DOM-объект, который похож на обычный DOM-узел, но им не является.
Синтаксис для его создания:
var fragment = document.createDocumentFragment();
В него можно добавлять другие узлы.
fragment.cloneNode(true); // клонирование с подэлементами
У DocumentFragment нет обычных свойств DOM-узлов, таких как innerHTML , tagName и т.п. Это не узел.
Его «Фишка» заключается в том, что когда DocumentFragment вставляется в DOM – то он исчезает, а вместо него вставляются его дети. Это свойство является уникальной особенностью DocumentFragment .
Например, если добавить в него много LI , и потом вызвать ul.appendChild(fragment) , то фрагмент растворится, и в DOM вставятся именно LI , причём в том же порядке, в котором были во фрагменте.
// хотим вставить в список UL много LI // делаем вспомогательный DocumentFragment var fragment = document.createDocumentFragment(); for (цикл по li) < fragment.appendChild(list[i]); // вставить каждый LI в DocumentFragment >ul.appendChild(fragment); // вместо фрагмента вставятся элементы списка
В современных браузерах эффект от такой оптимизации может быть различным, а на небольших документах иногда и отрицательным.
Понять текущее положение вещей вы можете, запустив следующий небольшой бенчмарк.
append/prepend, before/after, replaceWith
Сравнительно недавно в стандарте появились методы, которые позволяют вставить что угодно и куда угодно.
- 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, element)
- elem.insertAdjacentText(where, text)
- append/prepend – вставка в конец/начало.
- before/after – вставка перед/после.
- replaceWith – замена.
Задачи
Отсортировать таблицу
Имя Фамилия Отчество Возраст Вася Петров Александрович 10 Петя Иванов Петрович 15 Владимир Ленин Ильич 9 . . . . Строк в таблице много: может быть 20, 50, 100… Есть и другие элементы в документе.
Как бы вы предложили отсортировать содержимое таблицы по полю Возраст ? Обдумайте алгоритм, реализуйте его.
Как сделать, чтобы сортировка работала как можно быстрее? А если в таблице 10000 строк (бывает и такое)?
P.S. Может ли здесь помочь DocumentFragment ?
P.P.S. Если предположить, что у нас заранее есть массив данных для таблицы в JavaScript – что быстрее: отсортировать эту таблицу или сгенерировать новую?
Для сортировки нам поможет функция sort массива.
Общая идея лежит на поверхности: сделать массив из строк и отсортировать его. Тонкости кроются в деталях.
В ифрейме ниже загружен документ, описывающий и реализующий разные алгоритмы. Обратите внимание: разница в производительности может достигать нескольких раз!
P.S. Создавать DocumentFragment здесь ни к чему. Можно вытащить из документа TBODY и иметь дело с ним в отрыве от DOM (алгоритм 4).
P.P.S. Если нужно сделать много узлов, то обычно innerHTML работает быстрее, чем удаление и вставка элементов через DOM-вызовы. То есть, сгенерировать таблицу заново эффективнее.
Element.insertAdjacentHTML()
insertAdjacentHTML() разбирает указанный текст как HTML или XML и вставляет полученные узлы (nodes) в DOM дерево в указанную позицию. Данная функция не переписывает имеющиеся элементы, что предотвращает дополнительную сериализацию и поэтому работает быстрее, чем манипуляции с innerHTML .
Синтаксис
targetElement.insertAdjacentHTML(position, text);
Параметры
DOMString — определяет позицию добавляемого элемента относительно элемента, вызвавшего метод. Должно соответствовать одному из следующих значений (чувствительно к регистру):
- ‘beforebegin’ : до самого element (до открывающего тега).
- ‘afterbegin’ : сразу после открывающего тега element (перед первым потомком).
- ‘beforeend’ : сразу перед закрывающим тегом element (после последнего потомка).
- ‘afterend’ : после element (после закрывающего тега).
Строка, которая будет проанализирована как HTML или XML и вставлена в DOM дерево документа.
Наглядное отображение параметра position
Примечание: позиции beforebegin и afterend работают только если узел имеет родительский элемент.
Пример
//
onevar d1 = document.getElementById('one'); d1.insertAdjacentHTML('afterend', 'two'); // At this point, the new structure is: //onetwoПримечания
Соображения безопасности
Будьте осторожны при использовании вставки HTML на страницу с помощью insertAdjacentHTML() , не используете пользовательский ввод, который не был экранирован.
Не рекомендуется использовать insertAdjacentHTML() , когда требуется ввести простой текст. Используйте для этого свойство Node.textContent или метод Element.insertAdjacentText() . Они не будут интерпретировать текст как HTML, а вставят необработанный текст.
Спецификации
Совместимость с браузерами
BCD tables only load in the browser
Смотрите также
Found a content problem with this page?
This page was last modified on 19 окт. 2022 г. by MDN contributors.
Your blueprint for a better internet.
MDN
Support
Our communities
Developers
Visit Mozilla Corporation’s not-for-profit parent, the Mozilla Foundation.
Portions of this content are ©1998– 2023 by individual mozilla.org contributors. Content available under a Creative Commons license.Element.insertAdjacentElement()
Метод insertAdjacentElement() добавляет переданный элемент в DOM-дерево относительно элемента, вызвавшего метод.
Синтаксис
targetElement.insertAdjacentElement(position, element);
Параметры
DOMString — определяет позицию добавляемого элемента относительно элемента, вызвавшего метод. Должно соответствовать одному из следующих значений (чувствительно к регистру):
- ‘beforebegin’ : перед самим элементом targetElement .
- ‘afterbegin’ : внутри элемента targetElement , перед его первым потомком.
- ‘beforeend’ : внутри элемента targetElement , после его последнего потомка.
- ‘afterend’ : после самого элемента targetElement .
Элемент, добавляемый в DOM-дерево.
Возвращаемое значение
Метод возвращает добавляемый элемент, либо null , если добавление элемента завершилось ошибкой.
Исключения
Исключение Пояснение SyntaxError Переданное значение position не соответствует ни одному из допустимых. TypeError Передаваемый element не является валидным. Наглядное отображение параметра position
Примечание: значения beforebegin и afterend работают только если targetElement находится в DOM-дереве и имеет родительский элемент.
Примеры
.addEventListener('click', function() var tempDiv = document.createElement('div'); tempDiv.style.backgroundColor = randomColor(); if (activeElem) activeElem.insertAdjacentElement('beforebegin',tempDiv); > setListener(tempDiv); >); afterBtn.addEventListener('click', function() var tempDiv = document.createElement('div'); tempDiv.style.backgroundColor = randomColor(); if (activeElem) activeElem.insertAdjacentElement('afterend',tempDiv); > setListener(tempDiv); >);
Спецификация
Совместимость с браузерами
BCD tables only load in the browser
Смотрите также
Found a content problem with this page?
This page was last modified on 7 нояб. 2022 г. by MDN contributors.
Your blueprint for a better internet.
MDN
Support
Our communities
Developers
Visit Mozilla Corporation’s not-for-profit parent, the Mozilla Foundation.
Portions of this content are ©1998– 2023 by individual mozilla.org contributors. Content available under a Creative Commons license.