skip to Main Content

I just learned about DocumentFragment, but I’m not entirely sure how to use it with what I have so far. I have around 9K-10K rows in my table and it takes about 4 seconds to load the entire table.

for (const user of totalsArray)
{
    const truncatedUsername = truncateUsername(user);

    const newRow = table.insertRow(table.rows.length);

    const newCell0 = newRow.insertCell(USERNAME_IDX);
    const newCell1 = newRow.insertCell(TOTAL_COMMENTS_IDX);
    const newCell2 = newRow.insertCell(TOTAL_SCORE_IDX);
    const newCell3 = newRow.insertCell(TOTAL_NEG_COMMENTS_IDX);

    const userName = document.createTextNode(truncatedUsername);
    const totalComments = document.createTextNode(user[TOTAL_COMMENTS_IDX]);
    const totalScore = document.createTextNode(user[TOTAL_SCORE_IDX]);
    const totalNegatives = document.createTextNode(user[TOTAL_NEG_COMMENTS_IDX]);

    newCell0.appendChild(userName)
    newCell1.appendChild(totalComments);
    newCell2.appendChild(totalScore);
    newCell3.appendChild(totalNegatives);
}

From what I understand, you make the new fragment with new DocumentFragment(), then you append to it instead of the DOM, then when you are done with the main loop, you append the fragment to the DOM. But I am not seeing a way to do that with the way I am currently inserting cells into the row, and then appending a new text node to each cell. How does DocumentFragment fit in here?

2

Answers


  1. Document fragments are a great way to optimize performance while dealing with multiple DOM manipulations. Instead of appending each node individually to the table, you can use a document fragment to hold them temporarily before adding them to the DOM.

    MDN Article

    document.addEventListener('DOMContentLoaded', function() {
        // Dummy data
        const totalsArray = [
            { username: 'User1', totalComments: 10, totalScore: 100, totalNegComments: 2 },
            { username: 'User2', totalComments: 5, totalScore: 50, totalNegComments: 1 },
            // Add more data as needed
        ];
    
        const table = document.getElementById('myTable');
        const USERNAME_IDX = 0;
        const TOTAL_COMMENTS_IDX = 1;
        const TOTAL_SCORE_IDX = 2;
        const TOTAL_NEG_COMMENTS_IDX = 3;
    
        // Create a document fragment
        const fragment = document.createDocumentFragment();
    
        for (const user of totalsArray) {
            const truncatedUsername = user.username; // Assuming you have a function truncateUsername
    
            const newRow = table.insertRow(table.rows.length);
    
            const newCell0 = newRow.insertCell(USERNAME_IDX);
            const newCell1 = newRow.insertCell(TOTAL_COMMENTS_IDX);
            const newCell2 = newRow.insertCell(TOTAL_SCORE_IDX);
            const newCell3 = newRow.insertCell(TOTAL_NEG_COMMENTS_IDX);
    
            const userName = document.createTextNode(truncatedUsername);
            const totalComments = document.createTextNode(user.totalComments);
            const totalScore = document.createTextNode(user.totalScore);
            const totalNegatives = document.createTextNode(user.totalNegComments);
    
            // Append nodes to the fragment instead of directly to the cells
            fragment.appendChild(userName);
            fragment.appendChild(totalComments);
            fragment.appendChild(totalScore);
            fragment.appendChild(totalNegatives);
    
            // Append the fragment to the row after adding all nodes
            newCell0.appendChild(fragment.cloneNode(true));
            fragment.textContent = ''; // Clear the fragment for the next iteration
        }
    
        // Append the row to the table outside the loop
        table.appendChild(fragment);
    });
    <table id="myTable">
        <thead>
            <tr>
                <th>Username</th>
                <th>Total Comments</th>
                <th>Total Score</th>
                <th>Total Negative Comments</th>
            </tr>
        </thead>
        <tbody>
            <!-- Rows will be added dynamically here -->
        </tbody>
    </table>
    Login or Signup to reply.
  2. You won’t win much here.

    Given that everything is done synchronously it’s basically just the same action as if you were using a DocumentFragment already.
    And actually, you rarely win much in using DocumentFragment objects rather than dealing with the DOM directly (sometimes you even lose by doing so). The perfs boost from it is basically a myth from a decade ago. The browser just has to do the same things anyway when you append the fragment*.

    In some engines you may win by letting the parser deal with it entirely, i.e. by passing it an HTML markup trough .insertAdjacentHTML() or even .innerHTML. That should get the best built-in optimizations. Just beware you shouldn’t pass untrusted data in there as you’d be at risk of XSS attacks.

    But the best in such a case is to not render all the cells in one call.
    Instead use either pagination, or a scroll-based lazy-rendering.


    Ps: Here is a twitter thread from Jake Archibald about this very misconception with some benchmarks.


    * The only notable difference would be if you had an MutationObserver looking at the parent node, triggering one heavy action per MutationRecord, since you’d get them all separated instead of a single one in the case of a fragment.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search