3.8 KiB
Executable File

morphdom.js

A fast and lightweight DOM diffing/patching library for efficiently updating the DOM with minimal changes.

Basic Usage

morphdom(fromNode, toNode, options)     // Transform fromNode to match toNode

Returns the morphed node (may be a new node if root element changes). Instead of replacing entire DOM trees, morphdom intelligently compares and patches only the differences:

const container = document.getElementById('container');
const newHTML = '<div><p>Updated content</p></div>';

// Efficiently update DOM - only changed parts are modified
morphdom(container, newHTML);

Options

morphdom(fromNode, toNode, {
    childrenOnly: true,                 // Only morph children, not root element
    getNodeKey: node => node.id,        // Custom key function for matching nodes
    onBeforeNodeAdded: node => true,    // Called before adding new nodes
    onNodeAdded: node => {},            // Called after nodes are added
    onBeforeElUpdated: (from, to) => true, // Called before updating elements
    onElUpdated: node => {},            // Called after elements are updated
    onBeforeNodeDiscarded: node => true, // Called before removing nodes
    onNodeDiscarded: node => {},        // Called after nodes are removed
    onBeforeElChildrenUpdated: (from, to) => true, // Called before updating children
    skipFromChildren: (from, to) => false, // Skip children comparison
    addChild: (parent, child) => parent.appendChild(child) // Custom child addition
});

Key Matching

Use getNodeKey for efficient list updates:

// HTML with keyed elements
const html = `
    <ul>
        <li id="item-1">Item 1</li>
        <li id="item-2">Item 2</li>
        <li id="item-3">Item 3</li>
    </ul>
`;

morphdom(list, html, {
    getNodeKey: node => node.getAttribute('id')
});

Event Handlers

morphdom(container, newHTML, {
    onBeforeElUpdated: (fromEl, toEl) => {
        // Preserve event listeners
        if (fromEl.hasEventListeners) {
            return false; // Skip this element
        }
        return true;
    },
    
    onNodeAdded: (node) => {
        // Initialize new components
        if (node.classList?.contains('widget')) {
            initializeWidget(node);
        }
    }
});

Form Element Handling

Morphdom has special handling for form elements:

  • INPUT: Preserves checked, disabled, and value properties
  • TEXTAREA: Syncs value and text content
  • SELECT: Maintains selectedIndex and option states
  • OPTION: Handles selected state in select boxes

Common Patterns

Template Updates

function updateTemplate(container, data) {
    const template = `<div class="user">${data.name}</div>`;
    morphdom(container, template);
}

List Management

function updateList(listEl, items) {
    const html = items.map(item => 
        `<li id="item-${item.id}">${item.name}</li>`
    ).join('');
    
    morphdom(listEl, `<ul>${html}</ul>`, {
        childrenOnly: true,
        getNodeKey: node => node.id
    });
}

Component Preservation

morphdom(container, newHTML, {
    onBeforeElUpdated: (from, to) => {
        // Preserve components that haven't changed
        if (from.dataset.component === to.dataset.component) {
            return false; // Skip update
        }
        return true;
    }
});

Integration with uquery.js

This library is used by uquery's html() method when differential updates are enabled:

$.options.alwaysDoDifferentialUpdate = true;
$('#container').html(newContent); // Uses morphdom internally

Browser Support

  • Modern browsers with DOM Level 2+ support
  • Automatic fallbacks for older browsers
  • Template element support detection
  • Range API support detection