prk68
prk68

Reputation: 176

HTML insertBefore and layout/reflow management

I have a list which I want to update in response to event whose payload contains a new list item. I want to prepend the new list item to the list.

I am using the following logic to prepend the new 'msg' received from the event to the list.

<html>

<script>

    function addItem() {
        setInterval(() => {

            var ul = document.getElementById('mylist');
            var newItem = document.createElement('li')
            newItem.innerHTML = 'hello0.0'
            ul.insertBefore(newItem, ul.childNodes[0]);
        }, 1000);
    }

</script>


<body>
   <button onclick='addItem()'> </button>
    <ul id='mylist'>
        <li id="insertPoint">  hello1 </li>
        <li>  hello2 </li>
    </ul>

</body>
</html>

Now when I profile this on chrome, everytime the event happens there is a reflow which is expected. Chrome tells me that about 7 nodes need layout. This number is constant even when the list has hundreds of items.

Now my questions are

i) What are these 7 nodes that need layout ?

ii) The paint seems to done on the whole document as the list is directly attached to the body. So if the list has 1000 items, even though the layout happens for only 7 nodes, all the items still need to be repainted. Are there any smarter tactics to avoid this ?

Upvotes: 0

Views: 1118

Answers (1)

zer00ne
zer00ne

Reputation: 44010

Update

After having been provided with the HTML, I setup a test case that comprises of the following methods:

Overall speed is the bottom line, repaints is just one part of a bigger world that is the DOM. On Chrome both insertAdjacentElement/HTML were considerably faster than insertBefore() (by @10%), while in Firefox only marginally (by .55%).


i) What are these 7 nodes that need layout ?

Nodes can be elements, text, comments, whitespace, etc. so it depends on the context. It sounds trivial it being a constant of 7.

ii) The paint seems to done on the whole document as the list is directly attached to the body. So if the list has 1000 items, even though the layout happens for only 7 nodes, all the items still need to be repainted. Are there any smarter tactics to avoid this ?

The only way to avoid a full repaint of the page is AJAX AFAIK.

Try insertAdjacentHTML() although its name is a mouthful, it's a very fast and flexible method. Here's the signature:

element.insertAdjacentHTML( "position", string);

  • element: any element capable of holding content (i.e. has an end tag </*>)

  • "position": There four positions which are relative to the element:

    1. "beforebegin": The element to be inserted (i.e. the second parameter: string), would be placed in front of element. eq. insertBefore()

      • ex. <li>string</li> <ul>element</ul>
    2. "afterbegin": string would be placed within element in front of all of the children. eq. parentNode.prepend()

      • ex. <ul> <li>string</li> , <li>item</li>, <li>item</li> </ul>
    3. "beforeend": string is inserted inside element as the last child. eq. appendChild()

      • ex. <ul> <li>item</li>, <li>item</li>, <li>string</li> </ul>
    4. "afterend": string is placed after element. eq. doesn't exist and besides just using insertAdjacentHTML() the other alternative is too confusing I'm not going to bother to include it.

      • ex. <ul> <li>item</li>, <li>item</li> </ul> <li>string</li>
    5. string: This is a string that represents one or more elements to be rendered into HTML. eq. innerHTML

Demo

const btn = document.querySelector('.btn');
const li = `<li class='item'>Item</li>`;
const ul = document.querySelector('.ul');

btn.addEventListener('click', function(e) {
  ul.insertAdjacentHTML('afterbegin', li);
}, false);
<button class='btn'>ADD</button>
<ul class='ul'></ul>

Upvotes: 2

Related Questions