// This code uses ES2015.
// Please use a compatible browser like: Chrome, Opera, Firefox
function Seer (dataObj) {
let signals = {}
observeData(dataObj)
return {
data: dataObj,
observe,
notify
}
function observe (property, signalHandler) {
if(!signals[property]) signals[property] = []
signals[property].push(signalHandler)
}
function notify (signal) {
if(!signals[signal] || signals[signal].length < 1) return
signals[signal].forEach((signalHandler) => signalHandler())
}
function makeReactive (obj, key) {
let val = obj[key]
Object.defineProperty(obj, key, {
get () {
return val
},
set (newVal) {
val = newVal
notify(key)
}
})
}
function observeData (obj) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
makeReactive(obj, key)
}
}
// We can safely parse the DOM looking for bindings after we converted the dataObject.
parseDOM(document.body, obj)
}
function syncNode (node, observable, property) {
node.textContent = observable[property]
// We remove the `Seer.` as it is now available for us in our scope.
observe(property, () => node.textContent = observable[property])
}
function parseDOM (node, observable) {
const nodes = document.querySelectorAll('[s-text]')
for (const node of nodes) {
syncNode(node, observable, node.attributes['s-text'].value)
}
}
}
const App = Seer({
title: 'Game of Thrones',
firstName: 'Jon',
lastName: 'Snow',
age: 25
})
function updateText (property, e) {
App.data[property] = e.target.value
}
function resetTitle () {
App.data.title = "Game of Thrones"
}
<h1 s-text="title"></h1>
<div class="form-inline">
<div class="form-group">
<label for="title">Title: </label>
<input
type="text"
class="form-control"
id="title" placeholder="Enter title"
oninput="updateText('title', event)">
</div>
<button class="btn btn-default" type="button" onclick="resetTitle()">Reset title</button>
</div>
External resources loaded into this fiddle: