Skip to content

Commit bb2988d

Browse files
classify duck-typed parent into SlicedParentNode, and reimplement childNodes to be a live calculation based on original siblings.
1 parent 359e829 commit bb2988d

File tree

1 file changed

+73
-27
lines changed

1 file changed

+73
-27
lines changed

src/idiomorph.js

Lines changed: 73 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,8 +1198,9 @@ var Idiomorph = (function () {
11981198
if (newContent.parentNode) {
11991199
// we can't use the parent directly because newContent may have siblings
12001200
// that we don't want in the morph, and reparenting might be expensive (TODO is it?),
1201-
// so we create a duck-typed parent node instead.
1202-
return createDuckTypedParent(newContent);
1201+
// so instead we create a fake parent node that only sees a slice of its children.
1202+
/** @type {Element} */
1203+
return /** @type {any} */ (new SlicedParentNode(newContent));
12031204
} else {
12041205
// a single node is added as a child to a dummy parent
12051206
const dummyParent = document.createElement("div");
@@ -1218,33 +1219,78 @@ var Idiomorph = (function () {
12181219
}
12191220

12201221
/**
1221-
* Creates a fake duck-typed parent element to wrap a single node, without actually reparenting it.
1222+
* A fake duck-typed parent element to wrap a single node, without actually reparenting it.
1223+
* This is useful because the node may have siblings that we don't want in the morph, and it may also be moved
1224+
* or replaced with one or more elements during the morph. This class effectively allows us a window into
1225+
* a slice of a node's children.
12221226
* "If it walks like a duck, and quacks like a duck, then it must be a duck!" -- James Whitcomb Riley (1849–1916)
1223-
*
1224-
* @param {Node} newContent
1225-
* @returns {Element}
12261227
*/
1227-
function createDuckTypedParent(newContent) {
1228-
return /** @type {Element} */ (
1229-
/** @type {unknown} */ ({
1230-
childNodes: [newContent],
1231-
/** @ts-ignore - cover your eyes for a minute, tsc */
1232-
querySelectorAll: (s) => {
1233-
/** @ts-ignore */
1234-
const elements = newContent.querySelectorAll(s);
1235-
/** @ts-ignore */
1236-
return newContent.matches(s) ? [newContent, ...elements] : elements;
1237-
},
1238-
/** @ts-ignore */
1239-
insertBefore: (n, r) => newContent.parentNode.insertBefore(n, r),
1240-
/** @ts-ignore */
1241-
moveBefore: (n, r) => newContent.parentNode.moveBefore(n, r),
1242-
// for later use with populateIdMapWithTree to halt upwards iteration
1243-
get __idiomorphRoot() {
1244-
return newContent;
1245-
},
1246-
})
1247-
);
1228+
class SlicedParentNode {
1229+
/** @param {Node} node */
1230+
constructor(node) {
1231+
this.originalNode = node;
1232+
this.realParentNode = /** @type {Element} */ (node.parentNode);
1233+
this.previousSibling = node.previousSibling;
1234+
this.nextSibling = node.nextSibling;
1235+
}
1236+
1237+
/** @returns {Node[]} */
1238+
get childNodes() {
1239+
// return slice of realParent's current childNodes, based on previousSibling and nextSibling
1240+
const nodes = [];
1241+
let cursor = this.previousSibling
1242+
? this.previousSibling.nextSibling
1243+
: this.realParentNode.firstChild;
1244+
while (cursor && cursor != this.nextSibling) {
1245+
nodes.push(cursor);
1246+
cursor = cursor.nextSibling;
1247+
}
1248+
return nodes;
1249+
}
1250+
1251+
/**
1252+
* @param {string} selector
1253+
* @returns {Element[]}
1254+
*/
1255+
querySelectorAll(selector) {
1256+
return this.childNodes.reduce((results, node) => {
1257+
if (node instanceof Element) {
1258+
if (node.matches(selector)) results.push(node);
1259+
const nodeList = node.querySelectorAll(selector);
1260+
for (let i = 0; i < nodeList.length; i++) {
1261+
results.push(nodeList[i]);
1262+
}
1263+
}
1264+
return results;
1265+
}, /** @type {Element[]} */ ([]));
1266+
}
1267+
1268+
/**
1269+
* @param {Node} node
1270+
* @param {Node} referenceNode
1271+
* @returns {Node}
1272+
*/
1273+
insertBefore(node, referenceNode) {
1274+
return this.realParentNode.insertBefore(node, referenceNode);
1275+
}
1276+
1277+
/**
1278+
* @param {Node} node
1279+
* @param {Node} referenceNode
1280+
* @returns {Node}
1281+
*/
1282+
moveBefore(node, referenceNode) {
1283+
// @ts-ignore - use new moveBefore feature
1284+
return this.realParentNode.moveBefore(node, referenceNode);
1285+
}
1286+
1287+
/**
1288+
* for later use with populateIdMapWithTree to halt upwards iteration
1289+
* @returns {Node}
1290+
*/
1291+
get __idiomorphRoot() {
1292+
return this.originalNode;
1293+
}
12481294
}
12491295

12501296
/**

0 commit comments

Comments
 (0)