@@ -118,17 +118,28 @@ function onMutate(mutations) {
118118 return
119119 }
120120
121- let addedNodes = new Set
122- let removedNodes = new Set
121+ let addedNodes = [ ]
122+ let removedNodes = [ ]
123123 let addedAttributes = new Map
124124 let removedAttributes = new Map
125125
126126 for ( let i = 0 ; i < mutations . length ; i ++ ) {
127127 if ( mutations [ i ] . target . _x_ignoreMutationObserver ) continue
128128
129129 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+ } )
132143 }
133144
134145 if ( mutations [ i ] . type === 'attributes' ) {
@@ -170,42 +181,26 @@ function onMutate(mutations) {
170181 onAttributeAddeds . forEach ( i => i ( el , attrs ) )
171182 } )
172183
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+
173193 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
177195
178196 onElRemoveds . forEach ( i => i ( node ) )
179197 }
180198
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- } )
193199 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
197200 if ( ! node . isConnected ) continue
198201
199- delete node . _x_ignoreSelf
200- delete node . _x_ignore
201202 onElAddeds . forEach ( i => i ( node ) )
202- node . _x_ignore = true
203- node . _x_ignoreSelf = true
204203 }
205- addedNodes . forEach ( ( node ) => {
206- delete node . _x_ignoreSelf
207- delete node . _x_ignore
208- } )
209204
210205 addedNodes = null
211206 removedNodes = null
0 commit comments