ajaxdomdocumentfragment

createDocumentFragment() used in a for loop


Is createDocumentFragment() doing anything in the code below?

I'm trying to adapt this code. I'm not sure how it works and the for loop doesn't seem to call on tableFrag. Your insights?

function createTable(searchResults)
{
    var results_table = 
        document.getElementById("report_results").getElementsByTagName("table")[0];

    var newLink, tableFrag;
    tableFrag = document.createDocumentFragment();
    tableFrag.appendChild(results_table);

    for (result_index in searchResults.results)
    {
        newRow = results_table.getElementsByTagName("tbody")[0].insertRow(-1);

        newCell = newRow.insertCell(-1);
        newCell.appendChild(document.createTextNode(searchResults.results[result_index]["score"]));

        newCell = newRow.insertCell(-1);
        newLink = document.createElement("a");
        newLink.href = 
            "officer.php?officer_seq="+searchResults.results[result_index]["officer_seq"];
        newLink.appendChild(document.createTextNode(searchResults.results[result_index]["officer_id"]));
        newCell.appendChild(newLink);

        newRow = null;
    }
    document.getElementById("report_results").appendChild(tableFrag);
}

Specifically, what mystifies me is that tableFrag is not found in the loop:

    for (result_index in searchResults.results)
    {
        ...
        tableFrag this-or-that
        ...
    }
    document.getElementById("report_results").appendChild(tableFrag);

Solution

  • Yes, it's doing something: a performance optimization.

    The elements in the HTML document are kept in a tree data structure, which you can access via the DOM interface (ie methods like document.getElementById(id) or document.getElementsByClassName(). Each time this tree is manipulated, ie when you insert, remove or modify nodes via Javascript code, the browser's rendering engine must relayout boxes (this is also called a reflow) depending on the rules defined with CSS, and repaint the visible portion of the tree on the browser's window.

    In your case, you have to add a fixed number of rows to a table. When the loop starts, you already know that you have to add N rows to the table. If you simply add rows to the DOM element, every time you call table.appendChild(row) the browser will measure, layout and paint the page, and this happens at most N times (actually browsers throttles these costly operations, however this is not required by any spec, so we can assume for learning purposes that every time you append, the browser invalidates the tree and repaints).

    All of this processing is unneeded: we don't want the browser to visibly grow the table one row at a time, we simply want to put N rows in the table, in one single shot, kind of a transaction. DocumentFragment exists for this exact purpose: when you manipulate a fragment's children, the browser doesn't measure and layout anything. This only happens when you finally append the fragment to the main DOM. In our imaginary and simplified browser, the paint routines are called exactly once, instead of N times.

    So what your script does is creating a fragment, removing the table from the main DOM, adding the node to the fragment, manipulating the table while it's attached to the fragment, and finally adding the whole fragment back to the main DOM, when children are finally measured, laid out and painted. Note that the code doesn't explicitely removes the table from the DOM, but this is what the Node.appendChild(node) method does when the child node already belongs to some hierarchy:

    Adds a node to the end of the list of children of a specified parent node. If the node already exists it is removed from current parent node, then added to new parent node.

    You can read more about DocumentFragment on MDN.