From 47d03313691ea7cde62f440fc8867a45fa0b29d1 Mon Sep 17 00:00:00 2001 From: Daniel Hug Date: Wed, 20 Jan 2016 14:57:27 -0800 Subject: [PATCH] add live data-binding with Snoopy --- README.md | 39 ++++++++++++++++++++++++++++++++++++--- dom.js | 47 ++++++++++++++++++++++++++++++++++++++++++++--- index.html | 51 ++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 116 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 15c19d7..463fb93 100644 --- a/README.md +++ b/README.md @@ -54,12 +54,45 @@ var label = dom({ document.body.appendChild(label); ``` + +### Get live DOM updates with [Snoopy](https://github.com/Daniel-Hug/snoopy) + +1. include [Snoopy](https://github.com/Daniel-Hug/snoopy) before DOM builder: + + ```html + + ``` + +2. stick data in a Snoopy instance + + ```js + var counter = Snoopy({count: 0}); + ``` + +3. live-bind data with DOM-Builder + + ```js + var button = dom({ + el: 'button', + text: counter.snoop('count'), + on_click: function() { + counter.set('count', counter.count + 1); + } + }); + ``` + + ### node values Node values can be any of the following: - - **object:** creates an element - - **string:** creates a text node - - **DOM node without parent:** uses the node + - **object:** renders an element + - **string or number:** renders a text node + - **DOM node without parent:** renders the existing node + - **array of node values:** renders a document fragment + - **"snoopable" function ([Snoopy](#get-live-dom-updates) makes this easy):** + 1. should accept a callback + 2. call it right away passing a node value + 3. call it again whenever the node value should change ### properties diff --git a/dom.js b/dom.js index 525af16..9e3a7af 100644 --- a/dom.js +++ b/dom.js @@ -11,6 +11,15 @@ })(this, function() { 'use strict'; + function bindSet(val, setter) { + // if val is function it's snoopable so the setter should be passed to it. + if (typeof val === 'function') { + val(setter); + } else { + setter(val); + } + } + function createDocFrag(array) { // build each node and stick in docFrag var docFrag = document.createDocumentFragment(); @@ -21,6 +30,24 @@ return docFrag; } + + // snoopable fn should: + // - accept a callback + // - call it right away passing a node value + // - call it again whenever the node value should change + function createDataBoundNode(fn) { + var node; + fn(function(newVal) { + //if (node instanceof HTMLElement && newVal.el === node.tagName) { + // take props from newVal and stick on node + //} + var newNode = dom(newVal); + if (node) node.parentNode.replaceChild(newNode, node); + node = newNode; + }); + return node; + } + function createEl(elData) { var el = document.createElement(elData.el || 'div'); @@ -28,7 +55,10 @@ if (['el', 'text', 'kids'].indexOf(key) === -1) { // set JS properties if (key[0] === '_') { - el[key.slice(1)] = elData[key]; + var prop = key.slice(1); + bindSet(elData[key], function(newVal) { + el[prop] = newVal; + }); } // add event listener(s) @@ -46,12 +76,20 @@ } // add html attributes - else el.setAttribute(key, elData[key]); + else { + bindSet(elData[key], function(newVal) { + el.setAttribute(key, newVal); + }); + } } }); // set text - if (elData.text) el.textContent = elData.text; + if (elData.text) { + bindSet(elData.text, function(newVal) { + el.textContent = newVal; + }); + } // otherwise add child nodes else if (elData.kids) el.appendChild(createDocFrag(elData.kids)); @@ -72,6 +110,9 @@ // array -> document fragment Array.isArray(nodeData) ? createDocFrag(nodeData) : + // function -> data bound DOM node + type === 'function' ? createDataBoundNode(nodeData) : + // object -> element createEl(nodeData); }; diff --git a/index.html b/index.html index 8612521..d8a68e5 100644 --- a/index.html +++ b/index.html @@ -5,28 +5,49 @@ DOM Builder demo +