Narcissistic, Perfectly Modest

    TypeScript icon, indicating that this package has built-in type declarations

    1.3.8 • Public • Published

    Data binding without framework: a bunch of utilities that enable accessor-based reactivity for JavaScript objects.

    npm version Coverage Status

    If you're looking for defi-react get its README here.

    It can be installed via NPM:

    npm i defi
    const { bindNode, calc } = require('defi');
    bindNode(obj, 'key', node)

    Or downloaded to use as a global variable

    // use defi as a global variable
    defi.bindNode(obj, 'key', node)

    How would I use it?

    Skip this section if you're using defi-react because React handles DOM rendering by its own.

    As a simple task let's say you want to define a simple form with first name and last name input, where while you type a greeting appears.

    <input class="first">
    <input class="last">
    <output class="greeting"></output>
    // default data
    const obj = {
      first: 'John',
      last: 'Doe'
    // let's listen for first and last name changes
    defi.on(obj, 'change:first', () => console.log('First name is changed'));
    defi.on(obj, 'change:last', () => console.log('Last name is changed'));
    // we would like to re-calculate 'greeting' property every time
    // when the first or last are changed
    defi.calc(obj, 'greeting', ['first', 'last'], (first, last) => `Hello, ${first} ${last}`);
    // and we want to set up a two-way data binding between the props
    // and corresponding DOM nodes
    defi.bindNode(obj, {
      first: '.first',
      last: '.last',
      greeting: '.greeting'

    If first or last is changed then event handlers print info about that to console, greeting property is updated, .greeting element is populated by calculated data (by default "Hello, John Doe"). And it happens every time when these properties are changed and it doesn't matter which way. You can do obj.first = 'Jane' or you can type text into its field, and everything will happen immediately.

    That's the real accessor-based reactiveness! Check the example above here and try to type obj.first = 'Jane' at the "Console" tab.

    Note that if you want to use a custom HTML element (at the example above we use <output> tag) to update its innerHTML you will need to pass so-called "binder" as a rule of how the bound element should behave. By default defi.bindNode doesn't know how to interact with non-form elements.

    const htmlBinder = {
      setValue: (value, binding) => binding.node.innerHTML = value,
    // this will update innerHTML for any element when obj.greeting is changed
    defi.bindNode(obj, 'greeting', '.greeting', htmlBinder)

    Also you can use html from common-binders (a collection of binders of general purpose).

    const { html } = require('common-binders');
    defi.bindNode(obj, 'greeting', '.greeting', html())

    Note that there is also a routing library for defi.js - defi-router.

    Quick API ref

    Full reference with all variations, flags and tutorials live at

    • bindNode - Binds a property of an object to HTML node, implementing two-way data binding.
    // basic use (for standard HTML5 elements)
    defi.bindNode(obj, 'myKey', '.my-element');
    // custom use (for any custom element)
    defi.bindNode(obj, 'myKey', '.my-element', {
        // when is element state changed?
        // (that's a DOM event; a function can be used to listen to any non-DOM events)
        on: 'click',
        // how to extract element state?
        getValue: ({ node }) => someLibraryGetValue(node),
        // how to set element state?
        setValue: (v, { node }) => someLibrarySetValue(node, v),
        // how to initialize the widget?
        // it can be initialized in any way,
        // but 'initialize' function provides some syntactic sugar
        initialize: ({ node }) => someLibraryInit(node),
    obj.myKey = 'some value'; // updates the element
    • calc - Creates a dependency of one property value on values of other properties (including other objects).
    defi.calc(obj, 'a', ['b', 'c'], (b, c) => b + c);
    obj.b = 1;
    obj.c = 2;
    console.log(obj.a); // 3
    • mediate - Transforms property value on its changing.
    defi.mediate(obj, 'x', value => String(value));
    obj.x = 1;
    console.log(obj.x); // "1"
    console.log(typeof obj.x); // "string"
    • on - Adds an event handler. Detailed information about all possible events you can get at the "Events" section of the website.
    defi.on(obj, 'change:x', () => {
        alert(`obj.x now equals ${obj.x}`);
    obj.x = 1;
    • off - Deletes an event handler., ['change:x', 'bind']);
    defi.on(obj, ['foo', 'bar'], (a, b, c) => {
        alert(+ b + c);
    defi.trigger(obj, 'foo', 1, 2, 3); // alerts 6
    defi.bindNode(obj, 'myKey', '.my-element');
    defi.unbindNode(obj, 'myKey', '.my-element');
    • bound - Returns a bound element.
    defi.bindNode(obj, 'myKey', '.my-element');
    const node = defi.bound(obj, 'myKey'); // will return document.querySelector('.my-element')
    • chain - Allows chained calls of defi.js functions.
        .calc('a', 'b', b => b * 2)
        .set('b', 3)
        .bindNode('c', '.node');
    • defaultBinders - An array of functions which return a corresponding binder or a falsy value. This makes bindNode to detect how to bind a node if the binder argument isn't given.
    defi.defaultBinders.unshift(element => {
        // check if the element has "foo" class name
        if(element.classList.contains('foo')) {
            // if checking is OK, return a new binder
            return {
                on: ...,
                getValue: ...,
                setValue: ...
    // ...
    defi.bindNode(obj, 'myKey', '');
    const element = document.createElement('input');
    element.type = 'text';
    • remove - Deletes a property and removes attached change handlers.
    defi.remove(obj, 'myKey');
    • set - Sets a property value allowing to pass an event options object.
    defi.set(obj, 'myKey', 3, { silent: true });




    npm i defi

    DownloadsWeekly Downloads






    Unpacked Size

    302 kB

    Total Files


    Last publish


    • finom