@@ -114,6 +114,7 @@ var Idiomorph = (function () {
114114 * @property {ConfigInternal['callbacks'] } callbacks
115115 * @property {ConfigInternal['head'] } head
116116 * @property {HTMLDivElement } pantry
117+ * @property {Element[] } activeElementAndParents
117118 */
118119
119120 //=============================================================================
@@ -227,7 +228,10 @@ var Idiomorph = (function () {
227228
228229 const results = fn ( ) ;
229230
230- if ( activeElementId && activeElementId !== document . activeElement ?. id ) {
231+ if (
232+ activeElementId &&
233+ activeElementId !== document . activeElement ?. getAttribute ( "id" )
234+ ) {
231235 activeElement = ctx . target . querySelector ( `[id="${ activeElementId } "]` ) ;
232236 activeElement ?. focus ( ) ;
233237 }
@@ -306,17 +310,23 @@ var Idiomorph = (function () {
306310 }
307311
308312 // if the matching node is elsewhere in the original content
309- if ( newChild instanceof Element && ctx . persistentIds . has ( newChild . id ) ) {
310- // move it and all its children here and morph
311- const movedChild = moveBeforeById (
312- oldParent ,
313- newChild . id ,
314- insertionPoint ,
315- ctx ,
313+ if ( newChild instanceof Element ) {
314+ // we can pretend the id is non-null because the next `.has` line will reject it if not
315+ const newChildId = /** @type {String } */ (
316+ newChild . getAttribute ( "id" )
316317 ) ;
317- morphNode ( movedChild , newChild , ctx ) ;
318- insertionPoint = movedChild . nextSibling ;
319- continue ;
318+ if ( ctx . persistentIds . has ( newChildId ) ) {
319+ // move it and all its children here and morph
320+ const movedChild = moveBeforeById (
321+ oldParent ,
322+ newChildId ,
323+ insertionPoint ,
324+ ctx ,
325+ ) ;
326+ morphNode ( movedChild , newChild , ctx ) ;
327+ insertionPoint = movedChild . nextSibling ;
328+ continue ;
329+ }
320330 }
321331
322332 // last resort: insert the new node from scratch
@@ -426,7 +436,8 @@ var Idiomorph = (function () {
426436
427437 // if the current node contains active element, stop looking for better future matches,
428438 // because if one is found, this node will be moved to the pantry, reparenting it and thus losing focus
429- if ( cursor . contains ( document . activeElement ) ) break ;
439+ // @ts -ignore pretend cursor is Element rather than Node, we're just testing for array inclusion
440+ if ( ctx . activeElementAndParents . includes ( cursor ) ) break ;
430441
431442 cursor = cursor . nextSibling ;
432443 }
@@ -476,7 +487,9 @@ var Idiomorph = (function () {
476487 // If oldElt has an `id` with possible state and it doesn't match newElt.id then avoid morphing.
477488 // We'll still match an anonymous node with an IDed newElt, though, because if it got this far,
478489 // its not persistent, and new nodes can't have any hidden state.
479- ( ! oldElt . id || oldElt . id === newElt . id )
490+ // We can't use .id because of form input shadowing, and we can't count on .getAttribute's presence because it could be a document-fragment
491+ ( ! oldElt . getAttribute ?. ( "id" ) ||
492+ oldElt . getAttribute ?. ( "id" ) === newElt . getAttribute ?. ( "id" ) )
480493 ) ;
481494 }
482495
@@ -540,7 +553,9 @@ var Idiomorph = (function () {
540553 const target =
541554 /** @type {Element } - will always be found */
542555 (
543- ( ctx . target . id === id && ctx . target ) ||
556+ // ctx.target.id unsafe because of form input shadowing
557+ // ctx.target could be a document fragment which doesn't have `getAttribute`
558+ ( ctx . target . getAttribute ?. ( "id" ) === id && ctx . target ) ||
544559 ctx . target . querySelector ( `[id="${ id } "]` ) ||
545560 ctx . pantry . querySelector ( `[id="${ id } "]` )
546561 ) ;
@@ -558,7 +573,8 @@ var Idiomorph = (function () {
558573 * @param {MorphContext } ctx
559574 */
560575 function removeElementFromAncestorsIdMaps ( element , ctx ) {
561- const id = element . id ;
576+ // we know id is non-null String, because this function is only called on elements with ids
577+ const id = /** @type {String } */ ( element . getAttribute ( "id" ) ) ;
562578 /** @ts -ignore - safe to loop in this way **/
563579 while ( ( element = element . parentNode ) ) {
564580 let idSet = ctx . idMap . get ( element ) ;
@@ -998,6 +1014,7 @@ var Idiomorph = (function () {
9981014 idMap : idMap ,
9991015 persistentIds : persistentIds ,
10001016 pantry : createPantry ( ) ,
1017+ activeElementAndParents : createActiveElementAndParents ( oldNode ) ,
10011018 callbacks : mergedConfig . callbacks ,
10021019 head : mergedConfig . head ,
10031020 } ;
@@ -1038,6 +1055,24 @@ var Idiomorph = (function () {
10381055 return pantry ;
10391056 }
10401057
1058+ /**
1059+ * @param {Element } oldNode
1060+ * @returns {Element[] }
1061+ */
1062+ function createActiveElementAndParents ( oldNode ) {
1063+ /** @type {Element[] } */
1064+ let activeElementAndParents = [ ] ;
1065+ let elt = document . activeElement ;
1066+ if ( elt ?. tagName !== "BODY" && oldNode . contains ( elt ) ) {
1067+ while ( elt ) {
1068+ activeElementAndParents . push ( elt ) ;
1069+ if ( elt === oldNode ) break ;
1070+ elt = elt . parentElement ;
1071+ }
1072+ }
1073+ return activeElementAndParents ;
1074+ }
1075+
10411076 /**
10421077 * Returns all elements with an ID contained within the root element and its descendants
10431078 *
@@ -1046,7 +1081,8 @@ var Idiomorph = (function () {
10461081 */
10471082 function findIdElements ( root ) {
10481083 let elements = Array . from ( root . querySelectorAll ( "[id]" ) ) ;
1049- if ( root . id ) {
1084+ // root could be a document fragment which doesn't have `getAttribute`
1085+ if ( root . getAttribute ?. ( "id" ) ) {
10501086 elements . push ( root ) ;
10511087 }
10521088 return elements ;
@@ -1065,7 +1101,9 @@ var Idiomorph = (function () {
10651101 */
10661102 function populateIdMapWithTree ( idMap , persistentIds , root , elements ) {
10671103 for ( const elt of elements ) {
1068- if ( persistentIds . has ( elt . id ) ) {
1104+ // we can pretend id is non-null String, because the .has line will reject it immediately if not
1105+ const id = /** @type {String } */ ( elt . getAttribute ( "id" ) ) ;
1106+ if ( persistentIds . has ( id ) ) {
10691107 /** @type {Element|null } */
10701108 let current = elt ;
10711109 // walk up the parent hierarchy of that element, adding the id
@@ -1077,7 +1115,7 @@ var Idiomorph = (function () {
10771115 idSet = new Set ( ) ;
10781116 idMap . set ( current , idSet ) ;
10791117 }
1080- idSet . add ( elt . id ) ;
1118+ idSet . add ( id ) ;
10811119
10821120 if ( current === root ) break ;
10831121 current = current . parentElement ;
0 commit comments