@@ -118,17 +118,28 @@ function onMutate(mutations) {
118
118
return
119
119
}
120
120
121
- let addedNodes = new Set
122
- let removedNodes = new Set
121
+ let addedNodes = [ ]
122
+ let removedNodes = [ ]
123
123
let addedAttributes = new Map
124
124
let removedAttributes = new Map
125
125
126
126
for ( let i = 0 ; i < mutations . length ; i ++ ) {
127
127
if ( mutations [ i ] . target . _x_ignoreMutationObserver ) continue
128
128
129
129
if ( mutations [ i ] . type === 'childList' ) {
130
- mutations [ i ] . addedNodes . forEach ( node => node . nodeType === 1 && addedNodes . add ( node ) )
131
- mutations [ i ] . removedNodes . forEach ( node => node . nodeType === 1 && removedNodes . add ( node ) )
130
+ mutations [ i ] . removedNodes . forEach ( node => {
131
+ if ( node . nodeType !== 1 ) return
132
+ if ( ! node . _x_marker ) return
133
+
134
+ removedNodes . push ( node )
135
+ } )
136
+
137
+ mutations [ i ] . addedNodes . forEach ( node => {
138
+ if ( node . nodeType !== 1 ) return
139
+ if ( node . _x_marker ) return
140
+
141
+ addedNodes . push ( node )
142
+ } )
132
143
}
133
144
134
145
if ( mutations [ i ] . type === 'attributes' ) {
@@ -170,42 +181,26 @@ function onMutate(mutations) {
170
181
onAttributeAddeds . forEach ( i => i ( el , attrs ) )
171
182
} )
172
183
184
+ // There are two special scenarios we need to account for when using the mutation
185
+ // observer to init and destroy elements. First, when a node is "moved" on the page,
186
+ // it's registered as both an "add" and a "remove", so we want to skip those.
187
+ // (This is handled above by the ._x_marker conditionals...)
188
+ // Second, when a node is "wrapped", it gets registered as a "removal" and the wrapper
189
+ // as an "addition". We don't want to remove, then re-initialize the node, so we look
190
+ // and see if it's inside any added nodes (wrappers) and skip it.
191
+ // (This is handled below by the .contains conditional...)
192
+
173
193
for ( let node of removedNodes ) {
174
- // If an element gets moved on a page, it's registered
175
- // as both an "add" and "remove", so we want to skip those.
176
- if ( addedNodes . has ( node ) ) continue
194
+ if ( addedNodes . some ( i => i . contains ( node ) ) ) continue
177
195
178
196
onElRemoveds . forEach ( i => i ( node ) )
179
197
}
180
198
181
- // Mutations are bundled together by the browser but sometimes
182
- // for complex cases, there may be javascript code adding a wrapper
183
- // and then an alpine component as a child of that wrapper in the same
184
- // function and the mutation observer will receive 2 different mutations.
185
- // when it comes time to run them, the dom contains both changes so the child
186
- // element would be processed twice as Alpine calls initTree on
187
- // both mutations. We mark all nodes as _x_ignored and only remove the flag
188
- // when processing the node to avoid those duplicates.
189
- addedNodes . forEach ( ( node ) => {
190
- node . _x_ignoreSelf = true
191
- node . _x_ignore = true
192
- } )
193
199
for ( let node of addedNodes ) {
194
- // If the node was eventually removed as part of one of his
195
- // parent mutations, skip it
196
- if ( removedNodes . has ( node ) ) continue
197
200
if ( ! node . isConnected ) continue
198
201
199
- delete node . _x_ignoreSelf
200
- delete node . _x_ignore
201
202
onElAddeds . forEach ( i => i ( node ) )
202
- node . _x_ignore = true
203
- node . _x_ignoreSelf = true
204
203
}
205
- addedNodes . forEach ( ( node ) => {
206
- delete node . _x_ignoreSelf
207
- delete node . _x_ignore
208
- } )
209
204
210
205
addedNodes = null
211
206
removedNodes = null
0 commit comments