Precise DOM morphing
morphing typescript dom
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Trim child-visit hot path overhead

Remove dead sequence writes and replace spread NodeList copies with indexed array conversion to reduce per-morph allocations without changing matching behavior.

+13 -10
+13 -10
src/morphlex.ts
··· 466 466 if (!(this.#options.beforeChildrenVisited?.(from) ?? true)) return 467 467 const parent = from 468 468 469 - const fromChildNodes = [...from.childNodes] 470 - const toChildNodes = [...to.childNodes] 469 + const fromChildNodes = nodeListToArray(from.childNodes) 470 + const toChildNodes = nodeListToArray(to.childNodes) 471 471 472 472 candidateNodes.clear() 473 473 candidateElements.clear() ··· 476 476 unmatchedElements.clear() 477 477 whitespaceNodes.clear() 478 478 479 - const seq: Array<number> = [] 480 479 const matches: Array<number> = [] 481 480 const op: Array<Operation> = [] 482 481 const nodeTypeMap: Array<number> = [] ··· 530 529 if (candidate.isEqualNode(element)) { 531 530 matches[unmatchedIndex] = candidateIndex 532 531 op[unmatchedIndex] = Operation.EqualNode 533 - seq[candidateIndex] = unmatchedIndex 534 532 candidateElements.delete(candidateIndex) 535 533 unmatchedElements.delete(unmatchedIndex) 536 534 break ··· 551 549 if (localNameMap[unmatchedIndex] === candidateLocalNameMap[candidateIndex] && id === candidate.id) { 552 550 matches[unmatchedIndex] = candidateIndex 553 551 op[unmatchedIndex] = Operation.SameElement 554 - seq[candidateIndex] = unmatchedIndex 555 552 candidateElementsWithIds.delete(candidateIndex) 556 553 unmatchedElements.delete(unmatchedIndex) 557 554 break ··· 578 575 if (candidateIdSet.has(arrayId)) { 579 576 matches[unmatchedIndex] = candidateIndex 580 577 op[unmatchedIndex] = Operation.SameElement 581 - seq[candidateIndex] = unmatchedIndex 582 578 candidateElements.delete(candidateIndex) 583 579 unmatchedElements.delete(unmatchedIndex) 584 580 break candidateLoop ··· 606 602 (src && src === candidate.getAttribute("src"))) 607 603 ) { 608 604 matches[unmatchedIndex] = candidateIndex 609 - seq[candidateIndex] = unmatchedIndex 610 605 op[unmatchedIndex] = Operation.SameElement 611 606 candidateElements.delete(candidateIndex) 612 607 unmatchedElements.delete(unmatchedIndex) ··· 631 626 continue 632 627 } 633 628 matches[unmatchedIndex] = candidateIndex 634 - seq[candidateIndex] = unmatchedIndex 635 629 op[unmatchedIndex] = Operation.SameElement 636 630 candidateElements.delete(candidateIndex) 637 631 unmatchedElements.delete(unmatchedIndex) ··· 649 643 if (candidate.isEqualNode(node)) { 650 644 matches[unmatchedIndex] = candidateIndex 651 645 op[unmatchedIndex] = Operation.EqualNode 652 - seq[candidateIndex] = unmatchedIndex 653 646 candidateNodes.delete(candidateIndex) 654 647 unmatchedNodes.delete(unmatchedIndex) 655 648 break ··· 665 658 if (nodeType === candidateNodeTypeMap[candidateIndex]) { 666 659 matches[unmatchedIndex] = candidateIndex 667 660 op[unmatchedIndex] = Operation.SameNode 668 - seq[candidateIndex] = unmatchedIndex 669 661 candidateNodes.delete(candidateIndex) 670 662 unmatchedNodes.delete(unmatchedIndex) 671 663 break ··· 802 794 } 803 795 } 804 796 } 797 + } 798 + 799 + function nodeListToArray(nodeList: NodeListOf<ChildNode>): Array<ChildNode> 800 + function nodeListToArray(nodeList: NodeList): Array<ChildNode> 801 + function nodeListToArray(nodeList: NodeList): Array<ChildNode> { 802 + const length = nodeList.length 803 + const array = new Array<ChildNode>(length) 804 + for (let i = 0; i < length; i++) { 805 + array[i] = nodeList[i] as ChildNode 806 + } 807 + return array 805 808 } 806 809 807 810 function isInputElement(element: Element): element is HTMLInputElement {