I have a scenario which I find a bit complex: I want to schedule my changes to the DOM using requestAnimationFrame (rAF), but I have lots of conditional logic that I have to account for.
Consider this:
function writeToDOM(cb) {
fastdom.mutate(cb);
}
function performLogicThatDoesntUpdateTheDOM() {
// stuff I need to do with each touchmove event
// these things don't update the DOM, but they need to run
// after checking if the DOM needs to be updated
}
document.body.addEventListener('touchmove', event => {
if (propertyHasChanged) {
writeToDOM(function() {
someNode.classList.add('changed');
});
}
performLogicThatDoesntUpdateTheDOM();
if (anotherPropertyHasChanged) {
writeToDOM(function() {
anotherNode.classList.add('changed-too');
});
}
});
The idea is that I have a wrapper function (writeToDOM) that accepts a callback and queues that callback to be executed in the next available requestAnimationFrame using the fastdom library.
The touchmove event can fire multiple times in a frame so I want to make sure I only update the DOM inside rAF, even though I perform any other necessary computations every time the touchmove handler runs. The problem with the above approach is that it still schedules DOM updates every time the touchmove handler runs, so the rAF ends up getting overloaded with duplicated calls.
I already tried something like this:
let writesAreScheduled = false;
document.body.addEventListener('touchmove', event => {
if (writesAreScheduled) {
// don't do anything
return;
}
if (propertyHasChanged) {
writeToDOM(function() {
writesAreScheduled = false;
someNode.classList.add('changed');
});
writesAreScheduled = true;
}
performLogicThatDoesntUpdateTheDOM();
if (anotherPropertyHasChanged) {
writeToDOM(function() {
writesAreScheduled = false;
anotherNode.classList.add('changed-too');
});
writesAreScheduled = true;
}
});
The problem with this approach is that it doesn't allow me to run performLogicThatDoesntUpdateTheDOM once an update is scheduled.
So how can I limit only the DOM updates but always run my other logic? I don't want to throttle the entire touchmove handler, only the part that updates the DOM. The idea I have is to do something like this:
let writesAreScheduled = false;
document.body.addEventListener('touchmove', event => {
const taskQueue = [];
if (propertyHasChanged) {
taskQueue.push(function() {
writesAreScheduled = false;
someNode.classList.add('changed');
});
}
performLogicThatDoesntUpdateTheDOM();
if (anotherPropertyHasChanged) {
taskQueue.push(function() {
writesAreScheduled = false;
anotherNode.classList.add('changed-too');
});
}
// only schedule DOM updates if none are currently scheduled
if (!writesAreScheduled) {
taskQueue.forEach(task => writeToDOM(task));
writesAreScheduled = true;
}
});
Basically, I keep an array of DOM updates that need to be executed once I determine that they need to happen. Then after doing all the determinations, I check the flag to see if there's already a scheduled rAF, to make sure I only schedule one at a time. Remember that my objective is to limit only the frequency of the DOM updates, but run the other logic as frequently as the touchmove event fires.
I haven't implemented this yet, and the actual code I'm working with is a lot more complex so I haven't been able to create a simpler reproduction. Anyone have a different/better idea of how to do this? Thanks!