Convert HTML characters to DOM nodes and add them dynamically to the document

The dynamic conversion of strings to DOM nodes is often encountered in development, especially in template engines. Converting a string to a DOM node is not difficult in itself, and this article covers two main topics:

1 String conversion into HTML DOM node basic method and performance test 2 dynamically generated DOM node added to the document method and performance test

Example of this article: The following code snippet


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id='container'>
<! Class ='child'> XXX</div>
    </div>
</body>
</html>
Copy the code

The task is to write a JavaScript function that takes a text content, dynamically generates a div containing the text, and returns the Node.

1.1 Dynamically Creating a Node

1.1.1 innerHTML

In the first method, we use the document.createElement method to create a new element, then use innerHTML to inject a string into it, and finally return firstChild to create a dynamically created Node.

  <script>
        function createNode(txt) {
            const template = `<div class='child'>${txt}</div>`;
            let tempNode = document.createElement('div');
            tempNode.innerHTML = template;
            return tempNode.firstChild;
        }
        const container = document.getElementById('container');
        container.appendChild(createNode('hello'));

    </script>
Copy the code

Now let’s look at the second method

1.1.2 DOMParser

The parseFromString method of the DOMParser instance can be used to convert strings directly to document document objects. Now that we have document, we can manipulate it using various DOM apis.

  function createDocument(txt) {
            const template = `<div class='child'>${txt}</div>`;
            let doc = new DOMParser().parseFromString(template, 'text/html');
            let div = doc.querySelector('.child');
            return div;
        }
        
        const container = document.getElementById('container');
        container.appendChild(createDocument('hello'));
Copy the code

1.1.2 DocumentFragment

The DocumentFragment object represents a minimal document object that has no parent file. It is used as a lightweight version of Document to store formatted or poorly formatted XML fragments. The biggest difference is that the DocumentFragment is not part of the real DOM tree, and its changes do not cause reflow of the DOM tree or performance issues.

Using the document. CreateRange (). CreateContextualFragment method, we can directly convert a string into DocumentFragment objects.

 function createDocumentFragment(txt) {
            const template = `<div class='child'>${txt}</div>`;
            let frag = document.createRange().createContextualFragment(template);
            return frag;
        }

        const container = document.getElementById('container');
        container.appendChild(createDocumentFragment('hello'));
Copy the code

Note here that we insert the generated DocumentFragment object directly into the target node, which inserts all of its own points into the target node, excluding itself. We can also use it

frag.firstChild
Copy the code

To get the generated div.

1.1.3 Performance Test

Now let’s simply compare the performance of the above three methods. The test generates a single node, which is not necessarily of practical significance in actual use.

Test the createNode first.

  function createNode(txt) {
            const template = `<div class='child'>${txt}</div>`;

            let start = Date.now();
            for (let i = 0; i < 1000000; i++) {
                let tempNode = document.createElement('div');
                tempNode.innerHTML = template;
                let node = tempNode.firstChild;
            }
            console.log(Date.now() - start);

        }
        createNode('hello');
Copy the code

It took 6322 to test 1 million Node generation.

Let’s test the createDocument.

    function createDocument(txt) {
            const template = `<div class='child'>${txt}</div>`;
            let start = Date.now();
            for (let i = 0; i < 1000000; i++) {
                let doc = new DOMParser().parseFromString(template, 'text/html');
                let div = doc.firstChild;
            }
            console.log(Date.now() - start);
        }
    createDocument('hello');
Copy the code

Test 1 million Node generation, 55188 times.

Finally, test the createDocumentFragment.

 function createDocumentFragment(txt) {
            const template = `<div class='child'>${txt}</div>`;
            let start = Date.now();
            for (let i = 0; i < 1000000; i++) {
            let frag = document.createRange().createContextualFragment(template);
            }
            console.log(Date.now() - start);
        }
        createDocumentFragment();
Copy the code

It took 6,210 to test 1 million Node generation.

The createDocumentFragment and createNode methods were neck and neck in this round of testing. Let’s look at ways to dynamically add the generated DOM elements to the document.

1.2.0 Adding Nodes in Batches

Nodes that are created dynamically are most likely to be added to the document and displayed. Let’s introduce and compare several common solutions. The following methods for batch adding are createDocumentFragment methods.

1.2.1 directly append

The direct Append approach, which generates a node and adds it to the document, of course causing layout changes, is generally considered the worst performing approach.

 const template = "<div class='child'>hello</div>";

        function createDocumentFragment() {


            let frag = document.createRange().createContextualFragment(template);
            return frag;
        }
        // createDocumentFragment();
        const container = document.getElementById('container');
        let start = Date.now();
        for (let i = 0; i < 100000; i++) {
            container.appendChild(createDocumentFragment());
        }
        console.log(Date.now() - start);
Copy the code

In the code above we measure the dynamic addition of 100,000 nodes. The results are as follows:

It took 20 ms to test 1000 nodes, 10001 ms to test 10000 nodes, and 46549 ms to test 100000 nodes.

1.2.2 DocumentFragment

We’ve already seen a DocumentFragment, which converts strings. Let’s use this object as a temporary container to add multiple nodes at once.

Using the document. CreateDocumentFragment () method can create an empty DocumentFragment object.


        const template = "<div class='child'>hello</div>";

        function createDocumentFragment() {


            let frag = document.createRange().createContextualFragment(template);
            return frag;
        }
        // createDocumentFragment();
        const container = document.getElementById('container');
        let fragContainer = document.createDocumentFragment();
        let start = Date.now();
        for (let i = 0; i < 1000; i++) {
            fragContainer.appendChild(createDocumentFragment());
        }
        container.appendChild(fragContainer);
        console.log(Date.now() - start);
Copy the code

It took 25 ms to test 1000 nodes, 2877 ms to test 10000 nodes, and browser freezes on 100000 nodes.

1.3 summary

Several methods are simply introduced, and there is no technical content. However, from the perspective of dynamically adding nodes, the argument that the Performance of DocumentFragment method on the Internet is far better than that of direct Append is not true in my test scenario.

The correct scenario for using A DocumentFragment is as a virtual DOM container in scenarios where queries are frequently modified but no direct rendering is required.

For more exciting content, please follow the wechat subscription number “Xuanshuofront-end”.