Chapter 8 – DocumentFragment Nodes
最后更新于:2022-04-01 04:39:24
## 8.1 *DocumentFragment* object overview
The creation and use of a *DocumentFragment* node provides a light weight document DOM that is external to the live DOM tree. Think of a *DocumentFragment* as an empty document template that acts just like the live DOM tree, but only lives in memory, and its child nodes can easily be manipulated in memory and then appended to the live DOM.
## 8.2 Creating *DocumentFragment*'s using *createDocumentFragment()*
In the code below a *DocumentFragment* is created using *createDocumentFragment()* and *`<li>`*'s are appended to the fragment.
live code: [http://jsfiddle.net/domenlightenment/6e3uX](http://jsfiddle.net/domenlightenment/6e3uX)
~~~
<!DOCTYPE html>
<html lang="en">
<body>
<script>
var docFrag = document.createDocumentFragment();["blue", "green", "red", "blue", "pink"].forEach(function(e) { var li = document.createElement("li"); li.textContent = e; docFrag.appendChild(li);});console.log(docFrag.textContent); //logs bluegreenredbluepink
</script>
</body>
</html>
~~~
Using a *documentFragment* to create node structures in memory is extrememly efficent when it comes time to inject the *documentFragment* into live node structures.
You might wonder what is the advantage to using a *documentFragment* over simply creating (via*createElement()*) a *`<div>`* in memory and working within this *`<div>`* to create a DOM structure. The follow are the differences.
* A document fragment may contain any kind of node (except *`<body>`* or *`<html>`*) where as an element may not
* The document fragment itself, is not added to the DOM when you append a fragment. The contents of the node are. As opposed to appending an element node in which the element itself is part of the appending.
* When a document fragment is appended to the DOM it transfers from the document fragment to the place its appended. Its no longer in memory in the place you created it. This is not true for element nodes that are temperately used to contained nodes briefly and then are moved to the live DOM.
## 8.3 Adding a *DocumentFragment* to the live DOM
By passing the *appendChild()* and *insertBefore()* node methods a *documentFragment* argument the child nodes of the *documentFragment* are transported as children nodes to the DOM node the methods are called on. Below we create a *documentfragment*, add some *`<li>`*'s to it, then append these new element nodes to the live DOM tree using *appendChild()*.
live code: [http://jsfiddle.net/domenlightenment/Z2LpU](http://jsfiddle.net/domenlightenment/Z2LpU)
~~~
<!DOCTYPE html>
<html lang="en">
<body>
<ul></ul>
<script>
var ulElm = document.queryselector('ul');var docFrag = document.createDocumentFragment();["blue", "green", "red", "blue", "pink"].forEach(function(e) { var li = document.createElement("li"); li.textContent = e; docFrag.appendChild(li);});
ulElm.appendChild(docFrag);
//logs <ul><li>blue</li><li>green</li><li>red</li><li>blue</li><li>pink</li></ul>console.log(document.body.innerHTML);
</script>
</body>
</html>
~~~
### Notes
Document fragments passed as arguments to inserting node methods will insert the entire child node structure ignoring the documentFragment node itself.
## 8.4 Using *innerHTML* on a *documentFragment*
Creating a DOM structure in memory using node methods can be verbose and laboring. One way around this would be to created a *documentFragment*, append a *`<div>`* to this fragment because *innerHTML* does not work on document fragments, and then use the *innerHTML* property to update the fragment with a string of HTML. By doing this a DOM structure is crafted from the HTML string. In the code below I construct a DOM structure that I can then treat as a tree of nodes and not just a JavaScript string.
live code: [http://jsfiddle.net/domenlightenment/4W9sH](http://jsfiddle.net/domenlightenment/4W9sH)
~~~
<!DOCTYPE html>
<html lang="en">
<body>
<script>
//create a <div> and document fragment
var divElm = document.createElement('div');var docFrag = document.createDocumentFragment();
//append div to document fragmentdocFrag.appendChild(divElm);
//create a DOM structure from a string
docFrag.querySelector('div').innerHTML = '<ul><li>foo</li><li>bar</li></ul>';
//the string becomes a DOM structure I can call methods on like querySelectorAll()
//Just don't forget the DOM structure is wrapped in a <div>
console.log(docFrag.querySelectorAll('li').length); //logs 2
</script>
</body>
</html>
~~~
When it comes time to append a DOM structure created using a *documentFragment* and *`<div>`* you'll want to append the structure skipping the injection of the *`<div>`*.
live code: [http://jsfiddle.net/domenlightenment/kkyKJ](http://jsfiddle.net/domenlightenment/kkyKJ)
~~~
<!DOCTYPE html>
<html lang="en">
<body>
<div></div>
<script>
//create a <div> and document fragment
var divElm = document.createElement('div');var docFrag = document.createDocumentFragment();
//append div to document fragmentdocFrag.appendChild(divElm);
//create a DOM structure from a string
docFrag.querySelector('div').innerHTML = '<ul><li>foo</li><li>bar</li></ul>';
//append, starting with the first child node contained inside of the <div>
document.querySelector('div').appendChild(docFrag.querySelector('div').firstChild);
//logs <ul><li>foo</li><li>bar</li></ul>
console.log(document.querySelector('div').innerHTML);
</script>
</body>
</html>
~~~
### Notes
In addtion to *DocumentFragment* we also have *[DOMParser](http://html5.org/specs/dom-parsing.html#domparser)* to look forward too. *DOMParser* can parse HTML stored in a string into a DOM [Document](https://developer.mozilla.org/en/DOM/document "document"). It's only supported in Opera & Firefox as of today, but a [polyfill](https://gist.github.com/1129031) is avaliable. Of course, if you need a stand alone HTML to DOM script try [domify](https://github.com/component/domify).
## 8.5 Leaving a fragments containing nodes in memory by cloning
When appending a *documentFragment* the nodes contained in the Fragment are moved from the Fragment to the structure you are appending too. To leave the contents of a fragment in memory, so the nodes remain after appending, simply clone using *cloneNode()* the *documentFragment* when appending. In the code below instead of tranporting the *`<li>`*'s from the document fragment I clone the
*`<li>`*'s, which keeps the *`<li>`*'s being clonded in memory inside of the *documentFragment* node.
live code: [http://jsfiddle.net/domenlightenment/bcJGS](http://jsfiddle.net/domenlightenment/bcJGS)
~~~
<!DOCTYPE html>
<html lang="en">
<body>
<ul></ul>
<script>
//create ul element and document fragment
var ulElm = document.querySelector('ul');var docFrag = document.createDocumentFragment();
//append li's to document fragment["blue", "green", "red", "blue", "pink"].forEach(function(e) { var li = document.createElement("li"); li.textContent = e; docFrag.appendChild(li);});
//append cloned document fragment to ul in live DOM
ulElm.appendChild(docFrag.cloneNode(true));
//logs <li>blue</li><li>green</li><li>red</li><li>blue</li><li>pink</li>console.log(document.querySelector('ul').innerHTML);
//logs [li,li,li,li,li]
console.log(docFrag.childNodes);
</script>
</body>
</html>
~~~