Skip to content

Commit 47d0331

Browse files
committed
add live data-binding with Snoopy
1 parent f6d259a commit 47d0331

File tree

3 files changed

+116
-21
lines changed

3 files changed

+116
-21
lines changed

README.md

+36-3
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,45 @@ var label = dom({
5454
document.body.appendChild(label);
5555
```
5656

57+
58+
### Get live DOM updates with [Snoopy](https://github.com/Daniel-Hug/snoopy)
59+
60+
1. include [Snoopy](https://github.com/Daniel-Hug/snoopy) before DOM builder:
61+
62+
```html
63+
<script src="path/to/snoopy.js"></script>
64+
```
65+
66+
2. stick data in a Snoopy instance
67+
68+
```js
69+
var counter = Snoopy({count: 0});
70+
```
71+
72+
3. live-bind data with DOM-Builder
73+
74+
```js
75+
var button = dom({
76+
el: 'button',
77+
text: counter.snoop('count'),
78+
on_click: function() {
79+
counter.set('count', counter.count + 1);
80+
}
81+
});
82+
```
83+
84+
5785
### node values
5886

5987
Node values can be any of the following:
60-
- **object:** creates an element
61-
- **string:** creates a text node
62-
- **DOM node without parent:** uses the node
88+
- **object:** renders an element
89+
- **string or number:** renders a text node
90+
- **DOM node without parent:** renders the existing node
91+
- **array of node values:** renders a document fragment
92+
- **"snoopable" function ([Snoopy](#get-live-dom-updates) makes this easy):**
93+
1. should accept a callback
94+
2. call it right away passing a node value
95+
3. call it again whenever the node value should change
6396

6497

6598
### properties

dom.js

+44-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@
1111
})(this, function() {
1212
'use strict';
1313

14+
function bindSet(val, setter) {
15+
// if val is function it's snoopable so the setter should be passed to it.
16+
if (typeof val === 'function') {
17+
val(setter);
18+
} else {
19+
setter(val);
20+
}
21+
}
22+
1423
function createDocFrag(array) {
1524
// build each node and stick in docFrag
1625
var docFrag = document.createDocumentFragment();
@@ -21,14 +30,35 @@
2130
return docFrag;
2231
}
2332

33+
34+
// snoopable fn should:
35+
// - accept a callback
36+
// - call it right away passing a node value
37+
// - call it again whenever the node value should change
38+
function createDataBoundNode(fn) {
39+
var node;
40+
fn(function(newVal) {
41+
//if (node instanceof HTMLElement && newVal.el === node.tagName) {
42+
// take props from newVal and stick on node
43+
//}
44+
var newNode = dom(newVal);
45+
if (node) node.parentNode.replaceChild(newNode, node);
46+
node = newNode;
47+
});
48+
return node;
49+
}
50+
2451
function createEl(elData) {
2552
var el = document.createElement(elData.el || 'div');
2653

2754
Object.keys(elData).forEach(function(key) {
2855
if (['el', 'text', 'kids'].indexOf(key) === -1) {
2956
// set JS properties
3057
if (key[0] === '_') {
31-
el[key.slice(1)] = elData[key];
58+
var prop = key.slice(1);
59+
bindSet(elData[key], function(newVal) {
60+
el[prop] = newVal;
61+
});
3262
}
3363

3464
// add event listener(s)
@@ -46,12 +76,20 @@
4676
}
4777

4878
// add html attributes
49-
else el.setAttribute(key, elData[key]);
79+
else {
80+
bindSet(elData[key], function(newVal) {
81+
el.setAttribute(key, newVal);
82+
});
83+
}
5084
}
5185
});
5286

5387
// set text
54-
if (elData.text) el.textContent = elData.text;
88+
if (elData.text) {
89+
bindSet(elData.text, function(newVal) {
90+
el.textContent = newVal;
91+
});
92+
}
5593

5694
// otherwise add child nodes
5795
else if (elData.kids) el.appendChild(createDocFrag(elData.kids));
@@ -72,6 +110,9 @@
72110
// array -> document fragment
73111
Array.isArray(nodeData) ? createDocFrag(nodeData) :
74112

113+
// function -> data bound DOM node
114+
type === 'function' ? createDataBoundNode(nodeData) :
115+
75116
// object -> element
76117
createEl(nodeData);
77118
};

index.html

+36-15
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,49 @@
55
<title>DOM Builder demo</title>
66
</head>
77
<body>
8+
<script src="snoopy.js"></script>
89
<script src="dom.js"></script>
910

1011
<script>
11-
var input = dom({
12-
el: "input",
13-
type: "text",
14-
placeholder: "type something",
15-
_autofocus: true
12+
// setup observable data
13+
var counter = new Snoopy({count: 0});
14+
15+
// counter.even subscribes to counter.count
16+
counter.snoop('count', function(val) {
17+
counter.set('even', val % 2 === 0);
18+
});
19+
20+
counter.snoop('even', function(even) {
21+
counter.set('evenText', even ? 'even.' : 'not even.');
1622
});
17-
var output = dom({ el: 'output' });
1823

19-
document.body.appendChild(dom([
24+
// <button>0</button>
25+
var button = dom([
2026
{
21-
el: 'form',
22-
on_submit: function(event) {
23-
event.preventDefault();
24-
output.textContent = input.value ? ' Text: ' + input.value : '';
25-
},
26-
kids: [ input, { el: 'button', text: 'Go' } ]
27+
el: 'button',
28+
text: counter.snoop('count'),
29+
on_click: function() {
30+
counter.set('count', counter.count + 1);
31+
}
2732
},
28-
output
29-
]));
33+
{
34+
el: 'label',
35+
kids: [
36+
' ',
37+
{
38+
el: 'input',
39+
type: 'checkbox',
40+
_checked: counter.snoop('even'),
41+
_disabled: true
42+
},
43+
counter.snoop('count'), ' is ',
44+
counter.snoop('evenText')
45+
]
46+
}
47+
]);
48+
49+
// append to <body>
50+
document.body.appendChild(button);
3051
</script>
3152
</body>
3253
</html>

0 commit comments

Comments
 (0)