Search code examples
javascriptrequestanimationframe

Does requestAnimationFrame bring advantages when adding/removing DOM elements?


I have a function which goes through the direct child elements of a DOM node, and appends them to another freshly created Node, when they match a requirement. Then the function replaces the original DOM node with the one I created.

As appending the children to the new DOM node also removes them from the original DOM node, I wonder if I trigger a reflow on each removal, and if it will bring advantages if I wrap the function into requestAnimationFrame(). Will this then trigger a reflow only once?

I ask this because MDN only talk about animations and not about adding/removing DOM nodes.

For reference, this is the function:

function fix_paragraphs(old){
    cleanup_whitespace(old) // library function

    let fixed = create('div') // library function
    copy_attribs(old, fixed) // library function
    let brs = []
    let p = create('p')

    while (old.hasChildNodes()) {
        const child = old.firstChild
        const childtype = child.nodeType

        if (childtype === Node.ELEMENT_NODE && child.tagName === 'BR') {
            brs.push(child)
            old.removeChild(child)
        }

        else if (childtype === Node.ELEMENT_NODE && child.tagName === 'DIV') {
            if (p.hasChildNodes()) {
                fixed.appendChild(p)
            }
            p = create('p')
            fixed.appendChild(child)
            brs = []
        }

        else if ( (brs.length > 1) && (p.hasChildNodes()) ) {
            fixed.appendChild(p)
            p = create('p')
            p.appendChild(child)
            brs = []
        }

        else if (brs.length === 1) {
            p.appendChild(brs[0])
            p.appendChild(child)
            brs = []
        }

        else {
            p.appendChild(child)
            brs = []
        }
    }

    if (p.hasChildNodes()) {
        fixed.appendChild(p)
    }

    replace_with(old, fixed) // library function
}


Solution

  • I checked the function in Dev Tools and figured out, by inspecting the rasterize paint entries, how to make it run twice as fast, by first popping the parent element from which I remove nodes (old in the code). The updated code goes like this:

    function fix_paragraphs(old){
        let parent = old.parentNode
        let next = old.nextSibling
    
        old.remove() // pop it first
        cleanup_whitespace(old)
    
        let fixed = create('div')
        copy_attribs(old, fixed)
    
        let brs = []
        let p = create('p')
    
        while (old.hasChildNodes()) {
            const child = old.firstChild
            const childtype = child.nodeType
    
            if (childtype === ELEM && child.tagName === 'BR') {
                brs.push(child)
                old.removeChild(child)
            }
    
            else if (childtype === ELEM && child.tagName === 'DIV') {
                if (p.hasChildNodes()) {
                    fixed.appendChild(p)
                }
                fixed.appendChild(child)
                p = create('p')
                brs = []
            }
    
            else if ( (brs.length > 1) && (p.hasChildNodes()) ) {
                fixed.appendChild(p)
                p = create('p')
                p.appendChild(child)
                brs = []
            }
    
            else if (brs.length === 1) {
                p.appendChild(brs[0])
                p.appendChild(child)
                brs = []
            }
    
            else {
                p.appendChild(child)
                brs = []
            }
        }
    
        if (p.hasChildNodes()) {
            fixed.appendChild(p)
        }
    
        parent.insertBefore(fixed, next) // now insert it again
    }
    
    

    Thanks for the tips, dantechguy and Kaiido.