Precise DOM morphing
morphing typescript dom
TypeScript 93.1%
HTML 6.9%
Other 0.1%
326 1 0

Clone this repository

https://tangled.org/yippee.fun/morphlex https://tangled.org/did:plc:4crtcersdf55tzhbray4746o/morphlex
git@tangled.org:yippee.fun/morphlex git@tangled.org:did:plc:4crtcersdf55tzhbray4746o/morphlex

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

Morphlex

Morphlex is a ~3KB (gzipped) DOM morphing library that transforms one DOM tree to match another while preserving element state and making minimal changes.

What makes Morphlex different?#

  1. No cascading mutations from inserts. Simple inserts should be one DOM operation.
  2. No cascading mutations from removes. Simple removes should be one DOM operation.
  3. No cascading mutations from partial sorts. Morphlex finds the longest increasing subsequence for near perfect partial sorts.
  4. It uses moveBefore when available, preserving state.
  5. It uses isEqualNode, but in a way that is sensitive to the value of form inputs.
  6. It uses id sets, inspired by Idiomorph, so ids can help to identify their parent nodes.

Installation#

npm install morphlex

Or use it directly from a CDN:

<script type="module">
  import { morph } from "https://www.unpkg.com/morphlex@latest/dist/morphlex.min.js"
</script>

Usage#

import { morph, morphInner } from "morphlex"

// Morph the entire element
morph(currentNode, newNode)

// Morph only the children of the current node
morphInner(currentNode, newNode)

// Morph the entire document
morphDocument(document, newDocument)

Options#

Both morph and morphInner accept an optional third parameter for configuration:

morph(currentNode, newNode, {
  preserveChanges: true,
  beforeNodeAdded: (parent, node, insertionPoint) => {
    console.log("Adding node:", node)
    return true // return false to prevent addition
  },
})

Available Options#

  • preserveChanges: When true, preserves modified form inputs during morphing. This prevents user-entered data from being overwritten. Default: false

  • beforeNodeVisited: Called before a node is visited during morphing. Return false to skip morphing this node.

  • afterNodeVisited: Called after a node has been visited and morphed.

  • beforeNodeAdded: Called before a new node is added to the DOM. Return false to prevent adding the node.

  • afterNodeAdded: Called after a node has been added to the DOM.

  • beforeNodeRemoved: Called before a node is removed from the DOM. Return false to prevent removal.

  • afterNodeRemoved: Called after a node has been removed from the DOM.

  • beforeAttributeUpdated: Called before an attribute is updated on an element. Return false to prevent the update.

morph(currentNode, newNode, {
	beforeAttributeUpdated: (element, name) => {
		if (element.tagName === "DETAILS" && name === "open") return false
		return true
	},
})

This can be useful for preserving UI state that your backend does not track, such as whether a <details> element is open.

  • afterAttributeUpdated: Called after an attribute has been updated on an element.

  • beforeChildrenVisited: Called before an element's children are visited during morphing. Return false to skip visiting children.

  • afterChildrenVisited: Called after an element's children have been visited and morphed.