Javascript.RU

Польза от documentFragment

Update: Более новый материал по этой теме находится по адресу https://learn.javascript.ru/multi-insert.

В свое время читал много статей о documentFragment, но долго не мог понять, что в нем реально крутого.

Через некоторое время осознал, что ничего, но кое-что есть. Об этом и поговорим

documentFragment - "фрагмент документа" наиболее близок по смыслу к обычному DOM-элементу.

То есть, его можно создать:

var fragment = document.createDocumentFragment()

в него можно добавлять другие узлы.

fragment.appendChild(node)

Фишка заключается в том, что когда documentFragment вставляется в DOM - то он исчезает, а вместо него вставляются его дети. Это единственное и основное свойство documentFragment по сравнению со всеми остальными сущностями DOM.

То есть, можно добавить в него много TD, и потом append к TR. При этом фрагмент растворится и вставятся именно TD как прямые потомки.

Первый случай, когда documentFragment применим - это возврат множества узлов из функции. Можно это сделать возвращением массива, а можно вернуть documentFragment:

function makeRow() {
  var fragment = document.createDocumentFragment()
  var td_1 = document.createElement('TD')
  
  fragment.appendChild(td_1)
  ...
  fragment.appendChild(td_n)

  return fragment
}

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

Это, пожалуй, самый важный случай.

Допустим, нам нужно создать пачку элементов TR и вставить их в DOM.

Первый способ - создавать их и вставлять. И так n раз.

Но операция вставки в "живой" DOM дорогая. И тут на помощь приходит как раз documentFragment. Можно вставлять в него, а уже потом его добавить в DOM.

При этом скорость будет отличаться.

Совершенно не важно, используете ли вы в качестве оторванного от DOM контейнера documentFragment или что-то другое.

Важно, что промежуточные вставки идут в оторванный от живого документа DOM.

Например, вот два бенчмарка.

Оба они создают таблицу 10x10, наполняя TBODY элементами TR/TD.

При этом первый вставляет все в документ тут же, второй - задерживает вставку TBODY в документ до конца процесса.

Кликните, чтобы запустить.

/* appends elements right after creation */
appendFirst = new function() {
  var benchTable
  
  this.setup = function() {
    benchTable = document.getElementById('bench-table')
    while(benchTable.firstChild) { benchTable.removeChild(benchTable.firstChild) }
  }
  this.work = function() {
    var tbody = document.createElement('TBODY')

    benchTable.appendChild(tbody)

    for(var i=0; i<10; i++) {
      var tr = document.createElement('TR')
      tbody.appendChild(tr)
      for(var j=0; j<10; j++) {
        var td = document.createElement('td')
        td.appendChild(document.createTextNode(''+i+j))
        tr.appendChild(td)
      }      
    }
  }
  
}

/* appends elements right after creation
BUT wrapping tbody appended late */
appendLast = new function() {
  var benchTable
  
  this.setup = function() {
    benchTable = document.getElementById('bench-table')
    while(benchTable.firstChild) { benchTable.removeChild(benchTable.firstChild) }
  }
  this.work = function() {
    var tbody = document.createElement('TBODY')


    for(var i=0; i<10; i++) {
      var tr = document.createElement('TR')
      tbody.appendChild(tr)
      for(var j=0; j<10; j++) {
        var td = document.createElement('td')
        tr.appendChild(td)
        td.appendChild(document.createTextNode(''+i+j))
      }      
    }
    
    benchTable.appendChild(tbody)
  }
  
}

При чем же здесь documentFragment? А не при чем! Вместо него может быть любой узел, но иногда удобен именно documentFragment из-за своего свойства растворяться при вставке.

То есть, когда неудобно делать промежуточный узел-контейнер, например при добавлении множества TR, подгруженных с сервера. Вместо n вызовов appendChild в живой DOM имеем только 1 вызов, это может дать экономию такую же как в бенчмарках выше.

У documentFragment есть метод cloneNode, т.е. его можно клонировать вместе со всеми узлами.

Это также бывает удобно (1 вызов вместо n), но опять же - нет разницы, что клонировать - обычный узел вне DOM или documentFragment.

Удобство documentFragment лишь в том, что при вставке в документ он исчезнет. Как правило, это полезно в таблицах, гридах и т.п.

Фрагмент документа ничего не оптимизирует сам по себе, он не быстрее обычного документа.

Оптимизация заключается именно в том, что все действия делаются вне DOM.

Но благодаря свойству растворяться при вставке, documentFragment уникален, и его стоит иметь в виду. Не всегда можно и удобно создавать настоящий контейнер, а такой, временный - может очень даже пригодиться.


Автор: Дмитрий23 (не зарегистрирован), дата: 27 октября, 2010 - 19:03
#permalink

Спасибо за статью, раньше не знал про фрагменты.
Сейчас бегло прошёлся поиском по исходникам jQuery - фрагменты используются довольно активно. Так что могу предположить, что при использовании jQuery можно не беспокоиться о скорости вставки элементов.


Автор: Бирюков Павел (не зарегистрирован), дата: 4 ноября, 2010 - 18:38
#permalink

В тексте, который ниже бенчмарка три раза повторяется одно и то же. Прошу прощения, что комментарий не по сути.


Автор: cranx, дата: 18 октября, 2012 - 11:28
#permalink

Что-то у меня в хроме иногда первый бенчмарк (без фрагментов) быстрее второго


Автор: Александр Аникин (не зарегистрирован), дата: 3 июня, 2013 - 11:03
#permalink

Привет
Попробовал два теста. Оба выдали по 11. Что я должен был увидеть?
Браузер - Chrome на Mac OS X.

Спасибо.


Автор: Гость (не зарегистрирован), дата: 15 марта, 2014 - 15:55
#permalink

Firefox 27, Pentium 4 3.06GHz.
Неотложенный в среднем 19 мс.
Отложенный в среднем 11 мс.


Автор: Гость (не зарегистрирован), дата: 22 мая, 2014 - 12:31
#permalink

Chrome 34.0.1847.137

Оба теста одинаково перформят


Автор: Анонимоус (не зарегистрирован), дата: 9 августа, 2014 - 01:41
#permalink

можно из полученного (например ajax-ом) куска html сразу получить documentFragment

var range = document.createRange();
range.selectNode(document.body);
var df = range.createContextualFragment( responsedHtml );

Автор: aksdkjhasd (не зарегистрирован), дата: 17 октября, 2015 - 14:24
#permalink

Непосредственная - 5
Отложенная - 4

Firefox на OS X - MacBook Pro


Автор: superD (не зарегистрирован), дата: 26 апреля, 2016 - 23:39
#permalink

Google Chrome на OS X Mavericks:

Непосредственная - 6
Отложенная - 3


Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
11 + 3 =
Введите результат. Например, для 1+3, введите 4.
 
Текущий раздел
Поиск по сайту
Реклама
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Последние комментарии
Последние темы на форуме
Forum