I'm trying to avoid CSS Reflows,
usually DocumentFragment is enough for my needs.
I have cases when I modify/add/remove datasets,
The problem is that each dataset require one operation which causes reflow..
element.dataset is a read only object, so I wondered how can I do it with only one reflow instead of 3 in this example?
element.dataset.a='1'
delete element.b
element.dataset.c='2'
Does replacing the element completely is the only way to achieve this?
Changing the dataset of an element will not cause a synchronous reflow. You can do it as many times as you'd like during the same task.
To test if something causes a reflow or not, you can use a CSS transition.
Going from a known state, then setting an intermediary state, triggering what should cause the reflow and finally setting back the original state.
If the tested action did trigger a reflow, then a new transition from the intermediary state to the final one will happen. If no reflow did happen, then no transition will happen.
function testReflow(func) {
return new Promise( (res, rej) => {
const elem = document.querySelector(".reflow-tester");
// set "intermediary" values
elem.style.opacity = 1;
elem.style.transition = "none";
try { func(elem); } catch(err) { rej(err) }
elem.style.opacity = 0;
elem.style.transition = "opacity 0.01s";
// if the tested func does trigger a reflow
// the transition will start from 1 to 0
// otherwise it won't happen (from 0 to 0)
elem.addEventListener("transitionstart", (evt) => {
res(true); // let the caller know the result
}, { once: true });
// if the transition didn't start in 100ms, it didn't cause a reflow
setTimeout(() => res(false), 100);
});
}
(async () => {
// wait 1s before executing the tests to be sure we're not in weird first paint
await new Promise((res) => setTimeout(res, 1000));
// first testing with a well known reflow trigger
const offsetWidth = await
testReflow((elem)=>elem.offsetWidth);
console.log("offsetWidth getter:", offsetWidth);
// now with dataset
const dataset = await testReflow((elem) => {
elem.dataset.foo = "bar";
elem.dataset.bar = "baz";
elem.dataset.baz = "bla";
});
console.log("dataset:", dataset);
})().catch(console.error);
.reflow-tester {
opacity: 0;
}
<div class="reflow-tester">Tester</div>