JSFiddle - React, Tailwind, and code Playground
by Jeremy Likness
HTML
<div>
<h1>Data-Binding Example</h1>
<label for="firstName">
<div>First Name:</div><input type="text" data-bind="first" id="firstName" />
</label>
<label for="lastName">
<div>Last Name:</div><input type="text" data-bind="last" id="lastName"/>
</label>
<label for="fullName">
<div>Full Name:</div><input type="text" data-bind="full" id="fullName" disabled />
</label>
</div>
CSS
body {
padding: 5px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
label {
display: block;
}
label > div, input {
float: left;
width: 90vw;
}
JavaScript
const bindings = {};
const app = () => {
bindings.first = new Observable("Jeremy");
bindings.last = new Observable("");
bindings.full = new Computed(() => `${bindings.first.value} ${bindings.last.value}`.trim(), [bindings.first, bindings.last]);
applyBindings();
};
setTimeout(app, 0);
const applyBindings = () => {
document.querySelectorAll("[data-bind]").forEach(elem => {
const obs = bindings[elem.getAttribute("data-bind")];
bindValue(elem, obs);
});
}
const bindValue = (input, observable) => {
input.value = observable.value;
observable.subscribe(() => input.value = observable.value);
input.onkeyup = () => observable.value = input.value;
};
class Observable {
constructor(value) {
this._listeners = [];
this._value = value;
}
notify() {
this._listeners.forEach(listener => listener(this._value));
}
subscribe(listener) {
this._listeners.push(listener);
}
get value() {
return this._value;
}
set value(val) {
if (val !== this._value) {
this._value = val;
this.notify();
}
}
}
class Computed extends Observable {
constructor(value, deps) {
super(value());
const listener = () => {
this._value = value();
this.notify();
}
deps.forEach(dep => dep.subscribe(listener));
}
get value() {
return this._value;
}
set value(_) {
throw "Cannot set computed property";
}
}