I have a project which is responsible for managing the rendering of elements, but I'm running into a performance issue replacing elements and then focusing on whatever had focus before.
Below is a minimal example that replicates the performance issue:
const renderPage = () => {
// get the old section element
const oldSection = document.querySelector('section')
// create a new section element (we'll replaceWith later)
const newSection = document.createElement('section')
// create the render button
const newButton = document.createElement('button')
newButton.innerHTML = 'Render Page'
newButton.onclick = renderPage
newSection.appendChild(newButton)
// create a bunch of elements
const dummyDivs = [...new Array(100000)].forEach(() => {
const dummy = document.createElement('div')
dummy.innerHTML = 'dummy'
newSection.appendChild(dummy)
})
// replace the old page with the new one (causes forced reflow)
oldSection.replaceWith(newSection)
// reattach focus on the button (causes forced reflow)
newButton.focus()
}
window.renderPage = renderPage
<section>
<button onclick="renderPage()">Render</button>
</section>
When running this locally, I see the following in the performance report in Chrome/Edge
Both replaceWith
and focus
are triggering forced reflow. Is there a way to batch or group these actions so that only a single reflow occurs? I realize that there's no way to really get around this happening at all, but if I can batch them, I think that might improve my performance.
Indeed, focus always causes a reflow: What forces layout / reflow
So what you may do, is to reduce the reflowtime by inserting the new button standalone, initiate focus and after that you can append other childs:
Working example: Example
const renderPage = () => {
// get the old section element
const oldSection = document.querySelector('section')
// create a new section element (we'll replaceWith later)
const newSection = document.createElement('section')
// create the render button
const newButton = document.createElement('button')
newButton.innerHTML = 'Render Page'
newButton.onclick = renderPage
newSection.appendChild(newButton)
// create a bunch of elements
const dummies = []; // store in seperate array
const dummyDivs = [...new Array(100000)].forEach(() => {
const dummy = document.createElement('div')
dummy.innerHTML = 'dummy';
dummies.push(dummy)
})
// insert new section only with new button
oldSection.replaceWith(newSection)
newButton.focus(); // always causes reflow; but fast because it's only one element
// store all other nodes after focus
newSection.append(...dummies)
}
window.renderPage = renderPage