例えばTODOリストのデータの初期化などループで要素を追加するような処理では、DOMへの要素の追加はレンダリングコストが高いので、なるべくまとめて行いたいです。
jQuery でイベントをもつ複数の要素を丸っとDOMに追加する
jQuery時代は文字列結合でひとまとめにしたものをDOMに追加して、イベントは document.on
でイベントを発火させるターゲットを指定していれば、後から追加したテキストでもイベントを発火させることができました。
const data = [ {text: 'TASK 1', status: 0}, {text: 'TASK 2', status: 1}, //... ]; const initTodoList = (data) => { const todoList = document.getElementById('#todoList'); const listText = data.reduce((html, item) => { const className = item.status? 'complete' : 'incomplete'; return html += `<li class="${className}"> <label>${item.text}</label> <button class="doneBtn">DONE</button> </li>`; }, ''); todoList.inerHTML = listText; }; document.on('click.completeItem', '.doneBtn', onComplete); initTodoList();
JavaScript (VanillaJS) でイベントをもつ複数の要素を丸っとDOMに追加する
jQueryを使わない場合は document.addEvetListener
で取れるイベントから event.target
を遡って該当するイベントを発火させる事もできますが、少し処理が複雑になってしまいます。
各要素に addEventListener
でイベントを付けると、テキスト化してDOMに追加することができませんので、DOMElmentのまま追加するには appendChild()
を使いますが、これはリストなど複数の要素を渡すことができません。( jQuery こういう時便利でしたね… )
アンチパターン
ループ内で都度 DOM に追加するとレンダリングコストが高くなる
const data = [...]; const createItem = ({text, status}) => { const item = document.createElement('li'); const doneBtn = document.createElement('button'); item.className = status? 'complete' : 'incomplete'; item.innerHTML = `<label>${text}</label>`; doneBtn.className = 'doneBtn'; doneBtn.textContent = 'DONE'; doneBtn.addEventListener('click', onComplete); item.appendChild(doneBtn); return item; }; const initTodoList = (data) => { const todoList = document.getElementById('#todoList'); const listItems = data.map((item) => { const itemDOM = createItem(item); // 都度追加するのはレンダリングコストが高い todoList.appendChild(listItems); return itemDOM; }); // これはエラーになるのでループ内で都度DOMに追加する必要がある // todoList.appendChild(listItems); // => TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'. }; initTodoList();
document.createDocumentFragment を使う
Document.createDocumentFragment()
DocumentFragment
は DOM ノードです。メインの DOM ツリーの一部にはなりません。通常の使い方は、文書フラグメントを生成し、その文書フラグメントに要素を追加して、その文書フラグメントを DOM ツリーへ追加します。 DOM ツリー内では、文書フラグメントはすべての子要素によって置き換えられます。文書フラグメントはメモリ内にあり、メインの DOM ツリーの一部ではないため、文書フラグメントに子要素を追加してもページのリフロー (要素の位置と大きさを決定するための計算) が行われません。そのため文書フラグメントを利用することによって、パフォーマンスの改善が見込まれます。
cf. Document.createDocumentFragment() - Web API | MDN
DocumentFragment は後でまるっと追加したい要素を溜めておける透明な袋みたいなものっぽい。
const initTodoList = (data) => { const fragment = document.createDocumentFragment(); data.map((item) => { const itemDOM = createItem(item); fragment.appendChild(itemDOM); }); document.getElementById('#todoList') .appendChild(fragment); };
DocumentFragment は React で言う所の <></>
のようなものみたいで、描画されるDOMに追加しても、描画されず DocumentFragment 内に溜められていた要素が丸っと直接 appendChild
するDOM直下に追加され描画されました!
所感
createDocumentFragment
めっちゃ便利〜って調べたら結構昔からあった機能なのですね。
jQuery時代の後はReactとか触っててVanilla JSでゴリゴリやる機会も無かったので全く知らなかったです…
むしろこれがあったからこそ React の <Fragment></Fragment>
/<></>
ができたって訳だったのか〜
[参考]
- Document.createDocumentFragment() - Web API | MDN
- 七章第四回 ノードをまとめて扱う:DocumentFragment — JavaScript初級者から中級者になろう — uhyohyo.net
- Can I use... Support tables for HTML5, CSS3, etc

初めてのJavaScript 第3版 ―ES2015以降の最新ウェブ開発
- 作者: Ethan Brown,武舎広幸,武舎るみ
- 出版社/メーカー: オライリージャパン
- 発売日: 2017/01/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る

- アーティスト: 藍井エイル
- 出版社/メーカー: Sony Music Labels Inc.
- 発売日: 2019/04/17
- メディア: MP3 ダウンロード
- この商品を含むブログを見る