Skip to content

Commit 8b6c31c

Browse files
committed
Optimize mutation observer to better handle move operations - ref #4450
1 parent cb08c46 commit 8b6c31c

File tree

2 files changed

+36
-14
lines changed

2 files changed

+36
-14
lines changed

index.html

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,25 @@
1111
<!-- <script src="//cdn.tailwindcss.com"></script> -->
1212
<!-- <script src="//cdn.tailwindcss.com"></script> -->
1313

14-
<div x-data x-sort>
15-
<div x-sort:item >foo</div>
16-
<div >foo</div>
17-
<div x-sort:item >foo</div>
18-
</div>
14+
<script>
15+
let count = 0;
16+
document.addEventListener("alpine:init", () => {
17+
Alpine.directive("test", (el, _, { cleanup }) => {
18+
el.textContent = "Initialized";
19+
cleanup(() => {
20+
el.textContent = "Cleaned up";
21+
});
22+
// Alpine.nextTick(() => {
23+
// el.parentNode.replaceChildren(el);
24+
// })
25+
});
26+
});
27+
</script>
28+
<div x-data>
29+
<div x-ref="anchor"></div>
30+
31+
<div x-ref="target" class="bg-red-300 w-32 h-32" x-test></div>
1932

20-
<div x-data="{ val: true }"
21-
>
22-
<input type="text" x-model.boolean="val">
23-
<input type="checkbox" x-model.boolean="val">
24-
<input type="radio" name="foo" value="true" x-model.boolean="val">
25-
<input type="radio" name="foo" value="false" x-model.boolean="val">
33+
<button @click="$refs.anchor.before($refs.target)">Move</button>
2634
</div>
2735
</html>

packages/alpinejs/src/mutation.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,16 @@ export function flushAndStopDeferringMutations() {
112112
}
113113

114114
function onMutate(mutations) {
115+
console.log(mutations);
116+
115117
if (isCollecting) {
116118
deferredMutations = deferredMutations.concat(mutations)
117119

118120
return
119121
}
120122

121123
let addedNodes = []
122-
let removedNodes = []
124+
let removedNodes = new Set
123125
let addedAttributes = new Map
124126
let removedAttributes = new Map
125127

@@ -129,14 +131,26 @@ function onMutate(mutations) {
129131
if (mutations[i].type === 'childList') {
130132
mutations[i].removedNodes.forEach(node => {
131133
if (node.nodeType !== 1) return
134+
135+
// No need to process removed nodes that haven't been initialized by Alpine...
132136
if (! node._x_marker) return
133137

134-
removedNodes.push(node)
138+
removedNodes.add(node)
135139
})
136140

137141
mutations[i].addedNodes.forEach(node => {
138142
if (node.nodeType !== 1) return
139143

144+
// If the node is a removal as well, that means it's a "move" operation and we'll leave it alone...
145+
if (removedNodes.has(node)) {
146+
removedNodes.delete(node)
147+
148+
return
149+
}
150+
151+
// If the node has already been initialized, we'll leave it alone...
152+
if (node._x_marker) return;
153+
140154
addedNodes.push(node)
141155
})
142156
}
@@ -197,7 +211,7 @@ function onMutate(mutations) {
197211

198212
for (let node of addedNodes) {
199213
if (! node.isConnected) continue
200-
if (node._x_marker) return;
214+
201215
onElAddeds.forEach(i => i(node))
202216
}
203217

0 commit comments

Comments
 (0)