- From: Joe Pea <notifications@github.com>
- Date: Fri, 26 Aug 2022 17:17:28 -0700
- To: whatwg/dom <dom@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <whatwg/dom/issues/1105/1229056530@github.com>
# Userland Solution
Currently, MOs run all at once per target, which is highly irrepresentative of the order in which mutations happened in the DOM.
The end user has two options, both of which get more complicated:
1. Use a single MutationObserver root node of the current tree.
2. Implement some additional tracking across all MutationObservers on individual targets
Here is an example of option 2 in userland:
https://codepen.io/trusktr/pen/ExEBxmw/401b4869c60721e0fca18840036cbdee?editors=0010
# Possible Ideal Solutions (in the engine)
## 1 MutationObserver
I think the ideal solution would be for the browser engine to intelligently split MO changesets into chunks, and call them in the correct order (by calling MO callbacks for a single target one or more times) within the MO microtask, such that we can guarantee that each for loop in any callback is always iterating in the correct order relative to the whole tree in which the MutationObservers are observing.
This would eliminate the need for end users to write the complicated code required for the userland solutions.
## 2 Mutation Events
Fix DOM Mutation Events (make a new one with different names perhaps, or a new API that is similar to events).
-------
Finally, compare how drastically complicated this code,
```js
const connected = new Set
const disconnected = new Set
let scheduled = false
class XEl extends HTMLElement {
connectedCallback() {
this.o = new MutationObserver(changes => {
for (const change of changes) {
console.log('--- change', change.target)
for (const child of change.addedNodes) {
console.log('track added child', child)
connected.add(child)
}
for (const child of change.removedNodes) {
console.log('track removed child', child)
disconnected.add(child)
}
}
if (!scheduled) {
scheduled = true
queueMicrotask(() => {
console.log('--------------- MICROTASK MO')
scheduled = false
const allNodes = new Set([...connected, ...disconnected])
for (const child of allNodes) {
if (child.parentElement) {
if (disconnected.has(child)) console.log('child removed:', child)
if (connected.has(child)) console.log('child added:', child)
} else {
if (connected.has(child)) console.log('child added:', child)
if (disconnected.has(child)) console.log('child removed:', child)
}
}
connected.clear()
disconnected.clear()
})
}
})
this.o.observe(this, {childList: true})
}
disconnectedCallback() {
this.o.disconnect()
}
}
customElements.define('x-el', XEl)
setTimeout(() => {
queueMicrotask(() => console.log('--------------- MICROTASK before'))
const t = three
t.remove()
one.append(t)
queueMicrotask(() => console.log('--------------- MICROTASK after'))
}, 1000)
```
is compared to DOM Mutation Events code:
```js
const connected = new Set
const disconnected = new Set
let scheduled = false
const events = new Set()
class XEl extends HTMLElement {
lastConnected = new Set
childConnected = (event) => {
if (event.target.parentElement !== this) return
this.lastConnected.add(event.target)
console.log('child added:', this, event.target)
}
childDisconnected = (event) => {
if (!this.lastConnected.has(event.target)) return
this.lastConnected.delete(event.target)
console.log('child removed:', this, event.target)
}
connectedCallback() {
for (const child of this.children) this.lastConnected.add(child)
this.addEventListener('DOMNodeInserted', this.childConnected)
this.addEventListener('DOMNodeRemoved', this.childDisconnected)
}
disconnectedCallback() {
this.removeEventListener('DOMNodeInserted', this.childConnected)
this.removeEventListener('DOMNodeRemoved', this.childDisconnected)
}
}
customElements.define('x-el', XEl)
setTimeout(() => {
queueMicrotask(() => console.log('--------------- MICROTASK before'))
const t = three
t.remove()
one.append(t)
queueMicrotask(() => console.log('--------------- MICROTASK after'))
}, 1000)
```
And that's not even simple enough.
We didn't need to make a complicated API (MutationObserver) in order to have a deferred-batched event system.
--
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/dom/issues/1105#issuecomment-1229056530
You are receiving this because you are subscribed to this thread.
Message ID: <whatwg/dom/issues/1105/1229056530@github.com>
Received on Saturday, 27 August 2022 00:17:41 UTC