childList
records across multiple MutationObserver
s are observed in fundamentally incorrect order #1111
Description
Note Re-opening of #1105 because that issue was closed for not being succinct, although the problem was described fine. This time I will not include additional comments showing attempts to work around the problem.
I'm not asking for usage help.
I'm pointing out the difficulties involved with writing proper
MutationObserver
code for child tracking (across a tree, not just on a single element), and the intent of this issue is to spark ideas for a better API (again, not usage help).
Trying to port from DOM Mutation Events, or from dis/connectedCallbacks on children (which aren't available on builtin elements) to a MutationObserver
pattern that initializes or cleans up code based on when children are connected then disconnected (which works with built-in and custom elements), is nearly impossible without an immense and cumbersome effort.
I've been using MutationObserver
for years now, and am now discovering bugs in certain cases that I didn't notice before (I should have written better tests, but also MutationObserver
is complicated and cumbersome, the latter being my point and wishing to ideate a better API).
The Problem
Note I will only describe the problem here. #1105 has examples of complicated user code that attempt to work around the problem, but I'm not asking for help with those examples. I'm only trying to show the complexity of the problem, and wishing for a better API.
Note Also note
MutationObserver
is not bad for all its use cases, the problem I will describe here only relates to tracking children. In the ideal future world, a new API would replace thechildList
feature only, and that is the only scope of this issue.
In the following example, the connected and disconnected events fire in the wrong order, which leave connected children erroneously cleaned up:
https://codepen.io/trusktr/pen/oNqrNXE/3aad3bb7315877d00c7c42e3d77fed5a?editors=1010
In this one, the behavior is a bit different, but still not as desired:
https://codepen.io/trusktr/pen/MWVMWKe/091e6f303bd773f2754304fb1c9bff30?editors=1000
With standard connectedCallback
and disconnectedCallback
, they always fire in this order per each custom element when an element disconnected and re-connected:
disconnectedCallback()
connectedCallback()
However, with this naive MutationObserver
usage, the order (in the above two examples) when a child is removed then re-connected is
child connected
child removed
which will cause unexpected behavior, and will require people to start trying to work around the issue as I've described in further comments in #1105.
DOM Mutation Events are much much easier to use. MutationObserver
is incredibly difficult to work with. (wrt observing children)
Possible Solutions:
MutationObserver
event callbacks could be called in such a way thatchildList
records are perfectly interleaved in the same order as actions that happened in the DOM. This would complete solve the problem, and code would remain simple.- All the issues of Mutation Events could be fixed and exposed in a new event API.
- There is not any aspect of event patterns in general that make them inherently bad. It just happens to be that the way Mutation Events worked was not ideal and browsers implemented them differently with bugs. That's not to say we can't come up with an event pattern that works well.
- A new synchronous alternative to Mutation Events could be created that would be no worse than today's
connectedCallback
anddisconnectedCallback
methods (those are synchronous, and synchronicity is not the issue, but code ordering is), being an opt-in feature with no such thing as event bubbling in the way or the heaviness ofEventTarget
(Event
being designed specifically for a tree of nodes, when not all event patterns need to be associated with a tree at all).