DocumentFragment: what it is and how to (not) fight it

    Disclaimer
    Похоже, у меня начинается новая серия статей — немного скучная и сугубо утилитарная. В них будут содержаться разъяснения моментов, которые часто вызывают трудности у моих студентов. Если вы матёрый веб-девелопер, скорее всего, вам будет неинтересно. Если вы ждёте извращений в силе «Пятничного JS», их тут не будет, увы.


    One of the things that students regularly have problems understanding is DocumentFragment. In general, I can not blame them for it. With external simplicity, it has several unobvious and even counterintuitive properties. In this article I want to collect everything that a novice needs to know about him.

    image

    What it is


    A DocumentFragment is a container that can contain an arbitrary number of DOM elements. If quite simply, you can imagine it as a bucket. The elements are put into it, so that at the right moment they can be dumped at once where necessary.

    How to create


    Elementary.

    var fragment = document.createDocumentFragment();
    

    There are also other ways, but about them below.

    Why do you need


    As I wrote above, in order to store DOM elements. “But they can be stored in the usual diva,” the reader might argue. True, however, the fragment has a unique property that makes it the best candidate for this role. Consider the following code:

    var fragment = document.createDocumentFragment();
    var parentDiv = document.createElement("div");
    var div1 = document.createElement("div");
    var div2 = document.createElement("div");
    fragment.appendChild(div1);
    fragment.appendChild(div2);
    //сейчас будет интересно
    parentDiv.appendChild(fragment);
    console.log(parentDiv.children);
    

    What will the console tell us? A person who is not familiar with DocumentFragment might think that they parentDiv’ll have one child element fragment. But in fact, he will have two children - div1and div2. The point is that the fragment itself is not a DOM element, it is only a container for DOM elements. And when it is passed as an argument to the methods of the type appendChildor insertBefore, it is not embedded in the DOM tree, but instead it embeds its contents.

    And yet, why do you need it?


    The “bucket” property is, of course, good, but how is this useful in practice? DocumentFragment has two main areas of application.

    1. Storing pieces of HTML that have no common ancestor.

    There are situations when we need to replace the contents of an element, but do not touch the element itself. Suppose we use event delegation, and all event handlers that occur on internal elements are hung on an external div. In this case, we are ideally suited DocumentFragment:

    div.innerHTML = "";
    div.appendChild(fragmentWithAllContent);
    

    “But can we just add elements to the div as soon as they are created?” Asks a corrosive reader. We can, but it’s not worth doing that, and here’s why.

    2. Improved performance in the case of multiple inserts.

    The fact is that every time we change something in the active DOM tree, the browser has to do a lot of calculations. More information about this can be found here , for example . In this article we will limit ourselves to mentioning that there is such a terrible beast - reflow. When we add an element to the page, this beast wakes up and eats a piece of CPU time. If we add 100 elements in turn, the beast will wake up a hundred times and make a “bite” a hundred times. For the user, this may already be quite noticeable "hang".

    When we add an element to the DocumentFragment, it does not cause a reflow, because the fragment is not (and in principle cannot be) part of the active DOM tree. And the most important thing: when we insert the contents of a fragment using appendChildor other similar methods, no matter how many elements are inside the fragment, reflow is called only once .

    For clarity, I made a simple benchmark so that the reader could personally see the difference.

    Upd: comrade nuit told that for modern Chrome my words are no longer true. In it, reflow is not executed earlier than necessary, and thanks to this, the code without the DocumentFragment actually works fasterYes, and with other browsers is not so obvious. So before deciding whether to use fragments, profiling and research of the site’s target audience is necessary.

    Nuances


    There are two features due to which beginners often have difficulty using fragments. First, as I wrote above, the fragment is not a DOM element . This means that he lacks many familiar methods and properties, in particular - innerHTML. Therefore, you can not just turn the string into the contents of the fragment. How to do this is not easy, will be discussed below.

    The second feature: the fragment when using "spoils". More precisely - emptied. When we do div.appendChild(fragment), all the child elements of the fragment are transferred to div. And since an element cannot have more than one parent, this means that they are withdrawn from the fragment! To avoid this behavior when it is undesirable, you can use it cloneNode.

    <Template> tag


    There is one place where you can meet DocumentFragment, without creating it through JS. This is a property contentof the template element.

    The tag was <template>designed specifically to store pieces of HTML-code, but ahead of time not to load them with a browser. What is inside this tag does not become part of the active DOM tree. In particular (newcomers also often tinker with this), they cannot be found with the help querySelector. Elements created from the HTML code inside the tag <template>do not become its children. Instead, JavaScript can access them through a property contentthat is - a surprise! - just DocumentFragment'om.

    Using the template element, you can create a fragment from a string:

    functioncreateFragmentFromString(str){
        var template = document.createElement("template");
        template.innerHTML = str;
        return template.content;
    }
    

    Epilogue


    If you are new to web development, I hope you have learned a lot. If you are an experienced developer, you may want to add something to this article, in that case feel free to write about it in the comments. Thank you for reading, and have a nice day.

    Also popular now: