@@ -189,14 +189,6 @@ var Idiomorph = (function () {
189189 */
190190 function morphOuterHTML ( ctx , oldNode , newNode ) {
191191 const oldParent = normalizeParent ( oldNode ) ;
192-
193- // basis for calulating which nodes were morphed
194- // since there may be unmorphed sibling nodes
195- let childNodes = Array . from ( oldParent . childNodes ) ;
196- const index = childNodes . indexOf ( oldNode ) ;
197- // how many elements are to the right of the oldNode
198- const rightMargin = childNodes . length - ( index + 1 ) ;
199-
200192 morphChildren (
201193 ctx ,
202194 oldParent ,
@@ -205,10 +197,8 @@ var Idiomorph = (function () {
205197 oldNode , // start point for iteration
206198 oldNode . nextSibling , // end point for iteration
207199 ) ;
208-
209- // return just the morphed nodes
210- childNodes = Array . from ( oldParent . childNodes ) ;
211- return childNodes . slice ( index , childNodes . length - rightMargin ) ;
200+ // this is safe even with siblings, because normalizeParent returns a SlicedParentNode if needed.
201+ return Array . from ( oldParent . childNodes ) ;
212202 }
213203
214204 /**
@@ -238,7 +228,7 @@ var Idiomorph = (function () {
238228 const results = fn ( ) ;
239229
240230 if ( activeElementId && activeElementId !== document . activeElement ?. id ) {
241- activeElement = ctx . target . querySelector ( `# ${ activeElementId } ` ) ;
231+ activeElement = ctx . target . querySelector ( `[id=" ${ activeElementId } "] ` ) ;
242232 activeElement ?. focus ( ) ;
243233 }
244234 if ( activeElement && ! activeElement . selectionEnd && selectionEnd ) {
@@ -550,8 +540,9 @@ var Idiomorph = (function () {
550540 const target =
551541 /** @type {Element } - will always be found */
552542 (
553- ctx . target . querySelector ( `#${ id } ` ) ||
554- ctx . pantry . querySelector ( `#${ id } ` )
543+ ( ctx . target . id === id && ctx . target ) ||
544+ ctx . target . querySelector ( `[id="${ id } "]` ) ||
545+ ctx . pantry . querySelector ( `[id="${ id } "]` )
555546 ) ;
556547 removeElementFromAncestorsIdMaps ( target , ctx ) ;
557548 moveBefore ( parentNode , target , after ) ;
@@ -1200,8 +1191,9 @@ var Idiomorph = (function () {
12001191 if ( newContent . parentNode ) {
12011192 // we can't use the parent directly because newContent may have siblings
12021193 // that we don't want in the morph, and reparenting might be expensive (TODO is it?),
1203- // so we create a duck-typed parent node instead.
1204- return createDuckTypedParent ( newContent ) ;
1194+ // so instead we create a fake parent node that only sees a slice of its children.
1195+ /** @type {Element } */
1196+ return /** @type {any } */ ( new SlicedParentNode ( newContent ) ) ;
12051197 } else {
12061198 // a single node is added as a child to a dummy parent
12071199 const dummyParent = document . createElement ( "div" ) ;
@@ -1220,33 +1212,78 @@ var Idiomorph = (function () {
12201212 }
12211213
12221214 /**
1223- * Creates a fake duck-typed parent element to wrap a single node, without actually reparenting it.
1215+ * A fake duck-typed parent element to wrap a single node, without actually reparenting it.
1216+ * This is useful because the node may have siblings that we don't want in the morph, and it may also be moved
1217+ * or replaced with one or more elements during the morph. This class effectively allows us a window into
1218+ * a slice of a node's children.
12241219 * "If it walks like a duck, and quacks like a duck, then it must be a duck!" -- James Whitcomb Riley (1849–1916)
1225- *
1226- * @param {Node } newContent
1227- * @returns {Element }
12281220 */
1229- function createDuckTypedParent ( newContent ) {
1230- return /** @type {Element } */ (
1231- /** @type {unknown } */ ( {
1232- childNodes : [ newContent ] ,
1233- /** @ts -ignore - cover your eyes for a minute, tsc */
1234- querySelectorAll : ( s ) => {
1235- /** @ts -ignore */
1236- const elements = newContent . querySelectorAll ( s ) ;
1237- /** @ts -ignore */
1238- return newContent . matches ( s ) ? [ newContent , ...elements ] : elements ;
1239- } ,
1240- /** @ts -ignore */
1241- insertBefore : ( n , r ) => newContent . parentNode . insertBefore ( n , r ) ,
1242- /** @ts -ignore */
1243- moveBefore : ( n , r ) => newContent . parentNode . moveBefore ( n , r ) ,
1244- // for later use with populateIdMapWithTree to halt upwards iteration
1245- get __idiomorphRoot ( ) {
1246- return newContent ;
1247- } ,
1248- } )
1249- ) ;
1221+ class SlicedParentNode {
1222+ /** @param {Node } node */
1223+ constructor ( node ) {
1224+ this . originalNode = node ;
1225+ this . realParentNode = /** @type {Element } */ ( node . parentNode ) ;
1226+ this . previousSibling = node . previousSibling ;
1227+ this . nextSibling = node . nextSibling ;
1228+ }
1229+
1230+ /** @returns {Node[] } */
1231+ get childNodes ( ) {
1232+ // return slice of realParent's current childNodes, based on previousSibling and nextSibling
1233+ const nodes = [ ] ;
1234+ let cursor = this . previousSibling
1235+ ? this . previousSibling . nextSibling
1236+ : this . realParentNode . firstChild ;
1237+ while ( cursor && cursor != this . nextSibling ) {
1238+ nodes . push ( cursor ) ;
1239+ cursor = cursor . nextSibling ;
1240+ }
1241+ return nodes ;
1242+ }
1243+
1244+ /**
1245+ * @param {string } selector
1246+ * @returns {Element[] }
1247+ */
1248+ querySelectorAll ( selector ) {
1249+ return this . childNodes . reduce ( ( results , node ) => {
1250+ if ( node instanceof Element ) {
1251+ if ( node . matches ( selector ) ) results . push ( node ) ;
1252+ const nodeList = node . querySelectorAll ( selector ) ;
1253+ for ( let i = 0 ; i < nodeList . length ; i ++ ) {
1254+ results . push ( nodeList [ i ] ) ;
1255+ }
1256+ }
1257+ return results ;
1258+ } , /** @type {Element[] } */ ( [ ] ) ) ;
1259+ }
1260+
1261+ /**
1262+ * @param {Node } node
1263+ * @param {Node } referenceNode
1264+ * @returns {Node }
1265+ */
1266+ insertBefore ( node , referenceNode ) {
1267+ return this . realParentNode . insertBefore ( node , referenceNode ) ;
1268+ }
1269+
1270+ /**
1271+ * @param {Node } node
1272+ * @param {Node } referenceNode
1273+ * @returns {Node }
1274+ */
1275+ moveBefore ( node , referenceNode ) {
1276+ // @ts -ignore - use new moveBefore feature
1277+ return this . realParentNode . moveBefore ( node , referenceNode ) ;
1278+ }
1279+
1280+ /**
1281+ * for later use with populateIdMapWithTree to halt upwards iteration
1282+ * @returns {Node }
1283+ */
1284+ get __idiomorphRoot ( ) {
1285+ return this . originalNode ;
1286+ }
12501287 }
12511288
12521289 /**
0 commit comments