# domdiff [![donate](https://img.shields.io/badge/$-donate-ff69b4.svg?maxAge=2592000&style=flat)](https://github.com/WebReflection/donate) [![Coverage Status](https://coveralls.io/repos/github/WebReflection/domdiff/badge.svg?branch=master)](https://coveralls.io/github/WebReflection/domdiff?branch=master) [![Build Status](https://travis-ci.org/WebReflection/domdiff.svg?branch=master)](https://travis-ci.org/WebReflection/domdiff) [![License: ISC](https://img.shields.io/badge/License-ISC-yellow.svg)](https://opensource.org/licenses/ISC) A vDOM-less implementation of the [petit-dom](https://github.com/yelouafi/petit-dom) diffing logic, at the core of [hyperHTML](https://github.com/WebReflection/hyperHTML). ### V2 breaking change * the good old snabdom diff logic has been 100% replaced * lists with `null` or `undefined` nodes are not allowed anymore ... but I guess having null nodes in the equation was quite possibly a bad idea in the first place ... #### V2 Diffing Strategies: * common prefixes * common suffixes * skip same lists * add boundaries * remove boundaries * simple sub-sequences insertions and removals * one to many and many to one replacements * fast inverted list swap * O(ND) algo with a limit of 50 attempts * last fallback with a simplified Hunt Szymanski algorithm The current goal is to have in about 1K the best DOM diffing library out there. #### V1 breaking change The signature has moved from `parent, current[], future[], getNode(), beforeNode` to `parent, current[], future[], {before, compare(), node()}`. ### Signature ```js futureNodes = domdiff( parentNode, // where changes happen currentNodes, // Array of current items/nodes futureNodes, // Array of future items/nodes (returned) options // optional object with one of the following properties // before: domNode // compare(generic, generic) => true if same generic // node(generic) => Node ); ``` ### How to import it: * via **CDN**, as global variable: `https://unpkg.com/domdiff` * via **ESM**, as external module: `https://unpkg.com/domdiff/esm/index.js` * via **CJS**: `const EventTarget = require('domdiff').default;` ( or `require('domdiff/cjs').default` ) * via bundlers/transpilers: `import domdiff from 'domdiff';` ( or `from 'domdiff/esm'` ) ### Example ```js var nodes = { a: document.createTextNode('a'), b: document.createTextNode('b'), c: document.createTextNode('c') }; var parentNode = document.createElement('p'); var childNodes = [nodes.a, nodes.c]; parentNode.append(...childNodes); parentNode.textContent; // "ac" childNodes = domdiff( parentNode, childNodes, [nodes.a, nodes.b, nodes.c] ); parentNode.textContent; // "abc" ``` ### Compatibility: Every. JavaScript. Engine. ### A `{node: (generic, info) => node}` callback for complex data The optional `{node: (generic, info) => node}` is invoked per each operation on the DOM. This can be useful to represent node through wrappers, whenever that is needed. The passed `info` value can be: * `1` when the item/node is being appended * `0` when the item/node is being used as insert _before_ reference * `-0` when the item/node is being used as insert _after_ reference * `-1` when the item/node is being removed [Example](https://codepen.io/WebReflection/pen/bYJVPd?editors=0010) ```js function node(item, i) { // case removal or case after if ((1 / i) < 0) { // case removal if (i) { // if the item has more than a node // remove all other nodes at once if (item.length > 1) { const range = document.createRange(); range.setStartBefore(item[1]); range.setEndAfter(item[item.length - 1]); range.deleteContents(); } // return the first node to be removed return item[0]; } // case after else { return item[item.length - 1]; } } // case insert else if (i) { const fragment = document.createDocumentFragment(); fragment.append(...item); return fragment; } // case before else { return item[0]; } } const and = [document.createTextNode(' & ')]; const Bob = [ document.createTextNode('B'), document.createTextNode('o'), document.createTextNode('b') ]; const Lucy = [ document.createTextNode('L'), document.createTextNode('u'), document.createTextNode('c'), document.createTextNode('y') ]; // clean the body for demo purpose document.body.textContent = ''; let content = domdiff( document.body, [], [Bob, and, Lucy], {node} ); // ... later on ... content = domdiff( document.body, content, [Lucy, and, Bob], {node} ); // clean up domdiff( document.body, content, [], {node} ); ```