@gogocat/data-bind
v2.0.0
Published
dataBind is a small and fast MVVM tool for building front-end web
Readme
dataBind.js
Simple, fast, reactive data binding for modern browsers. No build tools required.
Why dataBind?
dataBind.js is not another front-end UI framework. It's a lightweight, pragmatic solution for adding reactive data binding to your existing HTML, without the complexity of modern frameworks.
Key Features
✨ Simple - Just HTML + JavaScript. No JSX, no virtual DOM, no build pipeline
⚡ Fast - Extremely small footprint (~15KB min+gzip) and high performance (see benchmarks)
🔄 Reactive - Automatic UI updates when data changes (like Vue 3, but simpler)
🎯 Focused - View + ViewModel pattern. No router, no state management, no opinions
🛠️ Zero Setup - Drop in a <script> tag and go. Works with any backend or framework
📦 Tiny - No dependencies, no build tools, no configuration
Quick Start
Installation
Via CDN:
<script src="https://unpkg.com/@gogocat/data-bind/dist/js/dataBind.min.js"></script>Via NPM:
npm install @gogocat/data-bindimport dataBind from '@gogocat/data-bind';Basic Example
HTML:
<div id="app">
<h1 data-bind-text="greeting"></h1>
<button data-bind-click="changeGreeting">Change</button>
</div>JavaScript:
const app = dataBind.init(document.getElementById('app'), {
greeting: 'Hello, World!',
changeGreeting() {
this.greeting = 'Hello, dataBind!';
// That's it! UI updates automatically in reactive mode
}
});
app.render();That's it! No JSX, no compilation, no complex setup.
Reactive State (Default)
By default, dataBind uses reactive mode - changes to data automatically update the UI:
const app = dataBind.init(element, {
counter: 0,
items: [],
increment() {
this.counter++; // UI updates automatically!
},
addItem() {
this.items.push({ text: 'New Item' }); // UI updates automatically!
}
});
app.render();How It Works
dataBind uses JavaScript Proxies to detect data changes:
// After init, use component.viewModel for reactive updates
app.viewModel.counter++; // ✅ Triggers automatic render
app.viewModel.items.push({...}); // ✅ Triggers automatic render
app.viewModel.user.name = 'Jane'; // ✅ Deep reactivity works!Manual Mode (Optional)
For maximum performance control, disable reactive mode:
const app = dataBind.use({ reactive: false }).init(element, viewModel);
// In manual mode, call render() explicitly
viewModel.counter++;
app.render(); // Manual render callCore Bindings
Text Binding
<h1 data-bind-text="title"></h1>
<p data-bind-text="user.name"></p> <!-- Deep paths supported -->Event Binding
<button data-bind-click="handleClick">Click Me</button>
<input data-bind-change="handleChange" data-bind-model="username">
<form data-bind-submit="handleSubmit">...</form>const viewModel = {
handleClick(event, element) {
console.log('Clicked!', event, element);
},
handleChange(event, element, newValue, oldValue) {
console.log('Changed from', oldValue, 'to', newValue);
}
};List Rendering
<div data-bind-for="item in items">
<p data-bind-text="item.name"></p>
<button data-bind-click="$root.deleteItem($index)">Delete</button>
</div>const viewModel = {
items: [
{ name: 'Item 1' },
{ name: 'Item 2' }
],
deleteItem(index) {
this.items.splice(index, 1); // Reactive update!
}
};Conditional Rendering
<!-- Removes from DOM when false -->
<div data-bind-if="isLoggedIn">
<p data-bind-text="user.name"></p>
</div>
<!-- Hides with CSS when false -->
<div data-bind-show="isVisible">
<p>Visible content</p>
</div>
<!-- Switch statement for multiple conditions -->
<div data-bind-switch="currentState">
<div data-bind-case="loading">Loading...</div>
<div data-bind-case="error">An error occurred</div>
<div data-bind-case="success">Data loaded successfully!</div>
<div data-bind-default>Please wait...</div>
</div>CSS Binding
<div data-bind-css="{ active: isActive, disabled: !isEnabled }"></div>
<div data-bind-css="dynamicClass"></div>Two-Way Data Binding
<input
type="text"
data-bind-model="username"
data-bind-change="onUsernameChange">The data-bind-model populates the input value from viewModel, while data-bind-change updates the viewModel when the input changes.
Advanced Features
Templates
<div data-bind-tmp="{id: 'userCard', data: 'user'}"></div>
<template id="userCard">
<div class="card">
<h2 data-bind-text="name"></h2>
<p data-bind-text="email"></p>
</div>
</template>Filters
<p data-bind-text="price | toDiscount | addGst"></p>const viewModel = {
price: 100,
toDiscount(value) {
return value * 0.9; // 10% discount
},
addGst(value) {
return value * 1.1; // Add 10% GST
}
};Component Communication (Pub/Sub)
// Component A: Subscribe to events
componentA.subscribe('USER_UPDATED', (userData) => {
console.log('User updated:', userData);
});
// Component B: Publish events
componentB.publish('USER_UPDATED', { name: 'John', email: '[email protected]' });AfterRender Callback
app.afterRender(() => {
console.log('Render completed!');
// Perform DOM operations, analytics, etc.
});Global Configuration
// Set global defaults for all components
dataBind.use({
reactive: true, // Enable reactive mode globally
trackChanges: false // Track individual property changes
});
// Or use chainable API
const app = dataBind
.use({ reactive: false })
.init(element, viewModel);Performance
dataBind is extremely fast. Try our benchmarks:
- DBMonster - dataBind (1000+ updates/sec)
- DBMonster - React (comparison)
- Fiber (Complex nested updates)
Compare with other frameworks.
Why So Fast?
- No Virtual DOM - Direct DOM updates with minimal overhead
- Efficient Diffing - Only updates changed elements
- Debounced Rendering - Batches multiple changes via requestAnimationFrame
- Tiny Size - ~15KB min+gzip (vs React 40KB+, Vue 33KB+)
Real-World Examples
- TodoMVC - Classic todo app
- Bootstrap Integration - Multi-component app
- Complex Lists - Nested templates
- Reactive Demo - Reactive state examples
Use Cases
Perfect For:
✅ Adding interactivity to server-rendered pages (PHP, .NET, Rails, etc.)
✅ Progressive enhancement of existing sites
✅ Rapid prototyping without build setup
✅ Small to medium web applications
✅ Projects where bundle size matters (eg Interactive Ad / widget on 3rd party web site)
✅ Teams that prefer vanilla JavaScript
Not Ideal For:
❌ Large single-page applications (consider Vue, React, Angular)
❌ Projects requiring full framework ecosystem (routing, state management, etc.)
❌ Micro-components smaller than a section/widget
Philosophy
dataBind follows these principles:
- Simplicity - HTML is the template, JavaScript is the logic. No new syntax to learn.
- Pragmatism - Leverage existing infrastructure. Work with what you have.
- Performance - Small, fast, and efficient. No bloat.
- Zero Dependencies - No build tools, no framework lock-in.
- Progressive Enhancement - Add reactivity where needed, keep it simple where possible.
API Overview
Initialization
dataBind.init(element, viewModel, options?)element: Root DOM elementviewModel: Plain JavaScript objectoptions:{ reactive: boolean, trackChanges: boolean }
Rendering
app.render(options?) // Returns PromiseReactive Updates
app.viewModel.property = value; // Automatic render in reactive modeLifecycle Hooks
app.afterRender(callback) // Called after each render
app.removeAfterRender(callback) // Remove specific callback
app.clearAfterRender() // Remove all callbacksEvents
app.subscribe(event, handler)
app.subscribeOnce(event, handler)
app.publish(event, data)
app.unsubscribe(event)
app.unsubscribeAll()Complete Binding Reference
| Binding | Purpose | Example |
|---------|---------|---------|
| data-bind-text | Display text content | <p data-bind-text="message"></p> |
| data-bind-click | Click event | <button data-bind-click="handleClick"></button> |
| data-bind-change | Change event (inputs) | <input data-bind-change="onChange"> |
| data-bind-model | Two-way binding | <input data-bind-model="username"> |
| data-bind-if | Conditional render | <div data-bind-if="isVisible"> |
| data-bind-show | Conditional display | <div data-bind-show="isVisible"> |
| data-bind-for | List rendering | <li data-bind-for="item in items"> |
| data-bind-css | Dynamic classes | <div data-bind-css="{ active: isActive }"> |
| data-bind-attr | Dynamic attributes | <img data-bind-attr="getImageAttrs"> |
| data-bind-tmp | Template rendering | <div data-bind-tmp="{id: 'tpl', data: 'user'}"> |
| data-bind-switch | Switch statement | <div data-bind-switch="state"> |
| data-bind-submit | Form submit | <form data-bind-submit="onSubmit"> |
| data-bind-blur | Blur event | <input data-bind-blur="onBlur"> |
| data-bind-focus | Focus event | <input data-bind-focus="onFocus"> |
| data-bind-dblclick | Double-click | <div data-bind-dblclick="onDblClick"> |
| data-bind-hover | Hover in/out | <div data-bind-hover="onHover"> |
Browser Support
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
Note: Reactive mode requires Proxy support (IE11 not supported for reactive mode, but manual mode works).
Documentation
- Configuration Guide - Global settings and options
- Examples - Live examples and demos
- API Reference - Complete API documentation
Migration from Manual to Reactive Mode
Before (Manual Mode):
viewModel.counter++;
app.render(); // Manual render callAfter (Reactive Mode - Default):
app.viewModel.counter++; // Automatic render!See CONFIGURATION.md for complete migration guide.
Contributing
Contributions welcome! Please read our contributing guidelines first.
License
Made with ❤️ by developers who love simplicity
