DOM blocking is something many people not familiar with JavaScript's strictly single-threaded synchronous execution model find out about the hard way, and it's usually just something we want to work around somehow (using timeouts, web-workers, etc). All well and good.
However, I would like to know if blocking of the actual user-visible rendering is something you can actually rely on. I'm 90% sure it is de facto the case in most browsers but I am hoping this isn't just a happily consistent accident. I can't seem to find any definitive statements from DOM specifications or even vendor documentation like MDM.
What worries me slightly is that while changes to the DOM are indeed not visible looking at the page, the internal DOM geometry (including CSS transforms and filters) does actually update during synchronous execution. For example:
console.log(element.getBoundingRect().width);
element.classList.add("scale-and-rotate");
console.log(element.getBoundingRect().width);
element.classList.remove("scale-and-rotate");
... will indeed report two different width values, though the page does not appear to flash. Synchronously waiting after the class is added (using a while loop) doesn't make the temporary changes visible either. Doing a Timeline trace in Chrome reveals that internally paint and re-paint is taking place just the same, which makes sense...
My concern is that, lacking a specific reason not, some browsers, like say, those dealing with underpowered mobile CPUs, may choose to actually reflect those internal calculations in the user-visible layout during that function's execution, and thus will result in an ugly "flash" during such temporary operations. So, more concretely, what I'm asking is: Do they have a specific reason not to?
(If you are wondering why I care about this at all, I sometimes need to measure calculated dimensions using getBoundingRect
for elements in a certain state to plan out spacing or animations or other such things, without actually putting them in that state or animating them first...)
log() yields this result isn't surprising because a DOM update is a synchronous event. When the properties of the DOM object are modified, that change is thrown onto the call stack, and no proceeding event can execute until the stack is empty again.
Blocking is when the execution of additional JavaScript in the Node. js process must wait until a non-JavaScript operation completes. This happens because the event loop is unable to continue running JavaScript while a blocking operation is occurring.
According to various sources, getting the position or size of a DOM element will trigger a reflow of the output if necessary, so that the returned values are correct. As a matter of fact, reading the offsetHeight
of an element has become a way to force a reflow, as reported by Alexander Skutin and Daniel Norton.
Paul Irish gives a list of several actions that cause a reflow. Among them are these element box metrics methods and properties:
- elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight,
- elem.offsetParent elem.clientLeft, elem.clientTop, elem.clientWidth,
- elem.clientHeight elem.getClientRects(), elem.getBoundingClientRect()
Stoyan Stefanov describes strategies used by browsers to optimize reflows (e.g. queueing DOM changes and performing them in batches), and adds the following remark:
But sometimes the script may prevent the browser from optimizing the reflows, and cause it to flush the queue and perform all batched changes. This happens when you request style information, such as
- offsetTop, offsetLeft, offsetWidth, offsetHeight
- scrollTop/Left/Width/Height
- clientTop/Left/Width/Height
- getComputedStyle(), or currentStyle in IE
All of these above are essentially requesting style information about a node, and any time you do it, the browser has to give you the most up-to-date value. In order to do so, it needs to apply all scheduled changes, flush the queue, bite the bullet and do the reflow.
There is nothing in Javascript related to concurrency that is anything but de facto. JS simply does not define a concurrency model. Everything is happy accident or years of consensus.
That said, if your function does not make any calls to weird things like XMLHttpRequest or "alert" or something like that, you can basically treat it as single-threaded with no interrupts.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With