Skate's DOM Diff is a virtual DOM library for diffing, patching and converting between virtual and real DOM trees.
- Serialise to and from read DOM trees
- Diff virtual trees and patch real trees
- Web worker support
npm install skatejs-dom-diff
Where options
are accepted, you may provide:
done
If specified, diffing is performed in a web worker and this callback is called when it's done.
Diffs two virtual trees.
/** @jsx h **/
import { diff, h } from 'skatejs-dom-diff';
const source = <div><span>source</span></div>;
const target = <div><span>target</span></div>;
const instructions = diff(source, target);
The patchInstructions
is an array
that can be passed to patch()
to update the source
tree. Before passing the instructions to patch()
, however, your source tree must be associated to real DOM nodes. This can be done by using mount()
or by converting them to a tree using toDom()
.
Creates a virtual fragment. You can pass nothing to create an empty fragment:
import { fragment } from 'skatejs-dom-diff';
const vFrag = fragment();
A single virtual node:
import { fragment } from 'skatejs-dom-diff';
const vFrag = fragment(<div />);
An array of virtual nodes:
import { fragment } from 'skatejs-dom-diff';
const vFrag = fragment([<div />, <span />]);
Or even a virtual fragment:
import { fragment } from 'skatejs-dom-diff';
const vFrag = fragment(fragment(<div />));
Creates a virtual node.
// <div class="my-class">text or...<span /></div>
h('div', { className: 'my-class' }, 'text or...', h('span'));
Or you could just use JSX:
/** @jsx h **/
// <div class="my-class">text or...<span /></div>
<div className="my-class">text or...<span /></div>
By default, h
only sets properties, but you can specify attributes you want to set by passing the special attributes
prop:
// <div class="my-class" />
<div attributes={{ class: 'my-class' }} />
You can pass aria-*
attributes using attributes
but you can also specify the aria
prop:
// <div aria-label="my label" />
<div aria={{ label: 'my label' }} />
Like the aria
prop, you can also use the data
prop:
// <div data-something="my data" />
<div data={{ something: 'my data' }} />
Events are bound using the special events
prop:
const click = e => doSomethingWith(e);
<div events={{ click }} />
The merge()
function is convenience for calling diff()
and patch()
sequentially. As with diff()
, you must ensure the source
virtual tree has been associated to real nodes first.
/** @jsx h **/
import { diff, h } from 'skatejs-dom-diff';
const source = <div><span>source</span></div>;
const target = <div><span>target</span></div>;
const dom = mount(source);
merge(source, target);
Mounts the vdom
to the real root
DOM node. It returns the root
node. If the root
node was not specified, it automatically creates a <div />
and returns it.
/** @jsx h **/
import { h, mount } from 'skatejs-dom-diff';
const div = mount(<p>some text</p>);
Is the same thing as:
/** @jsx h **/
import { h, mount } from 'skatejs-dom-diff';
const div = document.createElement('div');
mount(<p>some text</p>, div);
It's more than likely that you'll just mount it directly to the document:
/** @jsx h **/
import { h, mount } from 'skatejs-dom-diff';
mount(<p>some text</p>, document.getElementById('app'));
Takes instructiosn created using diff()
and performs them on the associated DOM nodes that each instructions is for.
/** @jsx h **/
import { diff, h, mount, patch } from 'skatejs-dom-diff';
const source = <p>source tree</p>;
const target = <p>target tree</p>;
const instructions = diff(source, target);
mount(source, document.getElementById('app'));
patch(instructions);
A highly convenient function for continually rendering a given template.
/** @jsx h **/
import { h, render } from 'skatejs-dom-diff';
const root = document.getElementById('app');
const renderer = render((root) => (
<p>{root.someProp}</p>
));
// Set the prop to render with
root.someProp = 'test 1';
// Initial render: <p>test 1</p>
renderer(root);
// Update the prop
root.someProp = 'test 2';
// Re-render: <p>test 2</p>
renderer(root);
Returns a virtual text node:
import { text } from 'skatejs-dom-diff';
const vText = text('my text node');
Convers a virtual tree to a real DOM tree, event listeners and all:
import { toDom } from 'skatejs-dom-diff';
const vdom = <p>I will soon be real!</p>
const dom = toDom(vdom);
// <p>I will soon be real!</p>
console.log(dom.outerHTML);
Converts a real DOM tree into a virtual tree. It only copies over attributes. Event listeners can't be copied because the standard DOM APIs don't provide a way to get bound listeners.
Properties currently aren't copied either, but is being worked on.
import { toVdom } from 'skatejs-dom-diff';
const dom = document.createElement('p');
dom.textContent = 'I will soon be fake!';
const vdom = toVdom(dom);
The types of patches that can occur. Currently these are:
import { types } from 'skatejs-dom-diff';
const {
APPEND_CHILD,
REMOVE_CHILD,
REMOVE_ATTRIBUTE,
REPLACE_CHILD,
SET_ATTRIBUTE,
SET_EVENT,
SET_PROPERTY,
TEXT_CONTENT
} = types;
You can tell the differ to do its work in a web worker simply by passing a done
callback option to any of the three major entry functions (diff()
, merge()
, render()
).
In the case of diff()
, it's called once the diffing algorithm has finished in the worker and passed the instructions
. The patch instructions
are the only argument passed into the callback.
/** @jsx h */
import { h, diff } from 'skatejs-dom-diff';
function done (instructions) {
patch(instructions);
}
diff(<p>source</p>, <p>target</p>, { done });
For done()
, it's passed in the same exact way. The only difference is that it's called after the patch is performed but it's still passed the instructions that were performed by the patch algorithm.
/** @jsx h */
import { h, merge } from 'skatejs-dom-diff';
function done (instructions) {
// The DOM has been updated, do what you want here.
}
merge(<p>source</p>, <p>target</p>, { done });
And for render()
, it is the same as the merge()
function. So once the vDOM is rendered and DOM is patched, done()
is called with the instructions that were performed.
import { h, render } from 'skatejs-dom-diff';
function done (instructions) {
// Renering and patching is done...
}
const root = document.createElement('div');
const doRender = render((root) => (
<div>{root.test}</div>
));
div.test = 'initial text';
doRender(div, done);
div.test = 'updated text';
doRender(div, done);