Simple Incremental DOM

by Alexey Zuev

HTML

<input type="text" value="Alexey">
<button>Increment</button>

CSS

body {
  padding: 20px;
  font-family: Helvetica;
}

JavaScript

const NODE_DATA_KEY = '__ID_Data__';

// The current nodes being processed
let currentNode = null;
let currentParent = null;

class NodeData {
  // key
  // attrs
  
  constructor(name) {
    this.name = name;
    this.text = null;
  }
}

function getData(node) {
  if (!node[NODE_DATA_KEY]) {
    node[NODE_DATA_KEY] = new NodeData(node.nodeName.toLowerCase());
  }

  return node[NODE_DATA_KEY];
}

function enterNode() {
  currentParent = currentNode;
  currentNode = null;
}

function nextNode() {
  currentNode = currentNode ? currentNode.nextSibling : currentParent.firstChild;
}

function exitNode() {
  currentNode = currentParent;
  currentParent = currentParent.parentNode;
}

const matches = function(matchNode, name/*, key */) {
  const data = getData(matchNode);
  return name === data.name // && key === data.key;
};

function renderDOM(name) {
  if (currentNode && matches(currentNode, name/*, key */)) {
    return currentNode;
  }

  const node = name === '#text' ? 
  	document.createTextNode('') :
    document.createElement(name);

  currentParent.insertBefore(node, currentNode);

  currentNode = node;

  return node;
}

function elementOpen(name) {
  nextNode();
  const node = renderDOM(name);
  enterNode();

  // check for updates, i.t attributes
  const data = getData(node);

  return currentParent;
}

function elementClose(node) {
  exitNode();

  return currentNode;
}

function text(value) {
  nextNode();
  const node = renderDOM('#text');

  // checks for text updates
  const data = getData(node);
  if (data.text !== value) {
    data.text = (value);
    node.data = value;
  }

  return currentNode;
}


function patch(node, fn, data) {
  currentNode = node;

  enterNode();
  fn(data);
  exitNode();
};


function render(data) {
  elementOpen('h1');
  {
    text('Hello, ' + data.user)
  }
  elementClose('h1');
  elementOpen('ul')
  {
    elementOpen('li'); 
    {
      text('Counter: ')
      elementOpen('span'); 
      {
        text(data.counter);
     ...