nanomorph Documentation

repository·master·Indexed 20 days ago

https://github.com/choojs/nanomorph

A hyper-fast diffing algorithm for real DOM nodes (version 5.4.3) used to efficiently patch the DOM by comparing an old tree of elements against a new one. It provides specialized handling for form elements, attribute and event synchronization, and optimization techniques using IDs, custom .isSameNode() methods, and the data-nanomorph-component-id attribute to control morphing behavior.

Tokens
1.9K
Snippets
9
Records
12
Agent score
73%

What's inside nanomorph

  1. Prevent morphing specific elements using component IDs

    master

    If you want to ensure two elements are replaced rather than morphed (common in custom component systems where different component types might both render as <div>), use the data-nanomorph-component-id attribute.

    nanomorph will only morph nodes if they share the exact same value for data-nanomorph-component-id. If the values differ, the old node is replaced with the new one.

    var el = html`<div data-nanomorph-component-id="a">hello</div>`
    var el2 = html`<div data-nanomorph-component-id="b">goodbye</div>`
    
    // This will result in replacement, not morphing
    assert.equal(nanomorph(el, el2), el2)
  2. Cache DOM elements using isSameNode()

    master

    You can prevent nanomorph from evaluating specific nodes (and their children) by implementing a custom .isSameNode() method on the DOM node. This is useful if you know a subtree hasn't changed or if another piece of code is managing that specific part of the DOM.

    var el = html`<div>node</div>`
    
    // tell nanomorph to not compare the DOM tree if they're both divs
    el.isSameNode = function (target) {
      return (target && target.nodeName && target.nodeName === 'DIV')
    }
  3. Optimize list reordering with IDs

    master

    When working with lists of elements, adding, removing, or reordering nodes can be expensive. To optimize this, add an id attribute to your DOM nodes. nanomorph will compare nodes with the same id against each other, which significantly reduces the number of re-renders required during reordering.

    var el = html`
      <section>
        <div id="first">hello</div>
        <div id="second">world</div>
      </section>
    `
  4. Clear input values in morphed elements

    master

    When using nanomorph with a template engine like nanohtml, you can clear the values of input elements using two methods:

    1. Set the value property to null.
    2. Omit the property entirely.
    html`<input class="beep" value=${null}>` // set the value to null
    html`<input class="beep">`               // omit property all together
  5. How morph() handles special form elements

    master

    To ensure a smooth user experience, nanomorph includes specialized logic for specific HTML elements where standard attribute syncing is insufficient:

    • <input>: Synchronizes checked, disabled, and indeterminate properties. It also handles the value property carefully: if the value attribute is present, it updates both the attribute and the property. It specifically avoids overwriting the value of type='file' inputs to prevent programmatic changes to file selections. For type='range', it updates the property to ensure the UI slider moves.
    • <option>: Synchronizes the selected property.
    • <textarea>: Synchronizes the value property and the underlying text node content.

    Special handling for 'null' or 'undefined' string values in attributes will result in the attribute being removed.

  6. Identify components using nanomorphComponentId

    master

    To ensure that specific elements are treated as the same component during the morphing process (preventing them from being replaced), you can use the data-nanomorph-component-id attribute.

    nanomorph checks the dataset.nanomorphComponentId of nodes. If two nodes have different component IDs, they are treated as different and the old one will be replaced by the new one.

  7. Attribute and Event synchronization in morph()

    master

    When morphing elements, nanomorph performs the following:

    1. Attributes: It iterates through the attributes of the newNode. If an attribute is new or changed, it updates the oldNode. It also removes any attributes from the oldNode that are no longer present on the newNode.
    2. Namespaced Attributes: It supports attribute namespace synchronization using setAttributeNS and removeAttributeNS.
    3. Events: It synchronizes a whitelist of event handlers (defined in the internal events module). If a new element has a whitelisted event, it is assigned to the oldNode. If the oldNode had an event that the newNode does not, it is set to undefined on the oldNode.
  8. Use nanomorph to diff and patch DOM trees

    master

    The nanomorph(oldTree, newTree) function compares two trees of HTML elements and applies the necessary patches to the oldTree to make it match the newTree.

    Warning: nanomorph will modify the newTree argument; you should discard the newTree object after the operation.

    var morph = require('nanomorph')
    var html = require('nanohtml')
    
    var tree = html`<div>hello people</div>`
    document.body.appendChild(tree)
    
    morph(tree, html`<div>nanananana-na-no</div>`)
    // document.body is now updated to match the new tree
  9. Configure nanomorph with the childrenOnly option

    master

    When calling nanomorph(oldTree, newTree, options), you can pass an options object to limit the scope of the morphing operation.

    If options.childrenOnly is set to true, nanomorph will skip comparing the root nodes themselves and will only update the children of the newTree within the oldTree. This is useful when you know the root element is already correct and you only want to sync its contents.

    nanomorph(oldTree, newTree, { childrenOnly: true });
  10. Morph one DOM tree into another with nanomorph()

    master

    The nanomorph function performs a highly efficient diff and patch operation between two DOM trees. It compares an oldTree to a newTree and applies the minimum necessary changes to the oldTree to make it match the newTree.

    Behavioral Logic:

    • No parent: If the trees are not the same, it replaces the old tree with the new one and returns the new tree.
    • Old node doesn't exist: It inserts the new node.
    • New node doesn't exist: It deletes the old node.
    • Nodes are different: It diffs the nodes and applies patches to the old node.
    • Nodes are the same: It walks all child nodes and appends/reorders them to the old node.

    Constraints:

    • oldTree and newTree must be objects (DOM nodes).
    • newTree cannot be a DocumentFragment (nodeType 11); it must have a single root node.
    const nanomorph = require('nanomorph');
    
    // Assuming oldTree and newTree are DOM elements
    nanomorph(oldTree, newTree);
  11. Morph the DOM with morph()

    master

    The morph(newNode, oldNode) function compares a new DOM node against an existing one and applies the necessary patches to the oldNode to make it match the newNode. This includes updating attributes, text content, comments, and handling special form elements like INPUT, OPTION, and TEXTAREA.

    // Assuming morph is imported from the package
    // newNode and oldNode are DOM nodes
    morph(newNode, oldNode);