SoFunction
Updated on 2025-03-10

Detailed explanation of bidirectional data binding in JavaScript

Bidirectional data binding refers to binding object property changes to the UI, or vice versa. In other words, if we have a user object with the name attribute, when we assign a new value to the UI, the new name will be displayed accordingly. Similarly, if the UI includes an input field to enter the user name, entering a new value will cause the property to change in the user object.

Many popular client-side JavaScript frameworks, such as AngularJS and KnockoutJS, regard bidirectional data binding as their number one feature. But this does not mean that it is difficult to implement bidirectional data binding from scratch. Similarly, when we need bidirectional data binding, we are not able to only choose one of these frameworks. The underlying idea of ​​two-way data binding is very basic, and it can be compressed into three steps:

1. We need a method to identify which UI element is bound to the corresponding attribute

2. We need to monitor changes in attributes and UI elements

3. We need to propagate all changes to the bound objects and elements

Although there are many ways to implement it, the easiest and most effective way is to use the publisher-subscriber model. The idea is simple: we can use custom data attributes to indicate bindings in HTML code. All bound JavaScript objects and DOM elements will "subscribe" to a publisher object. Any time a JavaScript object or an HTML input field is detected to change, we will proxy the event to the publisher-subscriber pattern, which in turn will broadcast the change and propagate it to all bound objects and elements.

Simple implementation using jQuery

Using jQuery to implement bidirectional data binding is very straightforward and simple, because this popular library can easily subscribe and publish DOM events, as well as our custom events:

function DataBinder(object_id){
 //Use a jQuery object as a simple subscriber publisher var pubSub = jQuery({});

 //We want a data element to indicate the binding in the form: data-bind-<object_id>="<property_name>"
 var data_attr = "bind-" + object_id,
   message = object_id + ":change";

 //Use the data-binding attribute and proxy to listen for changes on that element // so that changes can be "broadcast" to all associated objects
 jQuery(document).on("change","[data-" + data_attr + "]",function(evt){
  var input = jQuery(this);
  (message, [ $(data_attr),$()]);
 });

 //PubSub will propagate changes to all binding elements, set the input tag value or other tag HTML content
 (message,function(evt,prop_name,new_val){
  jQuery("[data-" + data_attr + "=" + prop_name + "]").each(function(){
  var $bound = jQuery(this);

  if($("input,text area,select")){
   $(new_val);
  }else{
   $(new_val);
  }
  });
 });

 return pubSub;
}

In this experiment, you can simply implement a User model according to the following code:

function User(uid){
 var binder = new DataBinder(uid),

  user = {
   atttibutes: {},

   //The property setter uses the data binder PubSub to publish changes
   set: function(attr_name,val){
    [attr_name] = val;
    (uid + ":change", [attr_name, val, this]);
   },

   get: function(attr_name){
    return [attr_name];
   },

   _binder: binder
  };

  (uid +":change",function(vet,attr_name,new_val,initiator){
   if(initiator !== user){
    (attr_name,new_val);
   }
  })
}

Now, no matter when we want to bind the model's properties to a part of the UI, we just need to set a suitable data property on the corresponding HTML element.

//JavaScript

var user = new User(123);
("name","Wolfgang");

//html

<input type="number" data-bind-123="name" /> 

The value of the input field will automatically reflect the name attribute of the user object and vice versa. The mission is completed!

Don't use jQuery to create two-way data binding

In most projects today, jQuery may have been used, so you can completely borrow the previous examples. But what if we go a step further, what if we remove the dependency on jQuery? In fact, this is not too difficult (especially when we restrict only IE8 or above versions). Ultimately, we need to use native JavaScript to implement a custom PubSub and observe DOM events.

function DataBinder(object_id){
 //Create a simple PubSub object
 var pubSub = {
  callbacks: {}.

  on: function(msg,calssback){
   [msg] = [msg] || [];
   [msg].push(callback);
  },

  publish: function(msg){
   [msg] = [msg] || [];
   for(var i = 0, len = [msg].length; i&lt;lenli++){
    [msg][i].apply(this,arguments);
   }
  }
 },

 data_attr = "data-bind-" + object_id,
 message = object_id + ":change",

 changeHandler = function(evt){
  var target =  || , //IE8 compatible   prop_name = (data_attr);

   if(prop_name &amp;&amp; prop_name !== ""){
    (message,prop_name,);
   }
 };

 // Listen to change events and proxy to PubSub if(){
  ("change",changeHandler,false);
 }else{
  //IE8 uses attachEvent instead of addEventListener  ("onchange",changeHandler);
 }

 //PubSub propagates changes to all bound elements
 (message,function(vet,prop_name,new)_val){
  var elements = ("[" + data_attr + "=" + prop_name + "]"),
    tah_name;

  for(var i = 0,len =; i &lt; len; i++){
   tag_name = elements[i].();

   if(tag_name === "input" || tag_name === "textarea" || tag_name === "select"){
   elements[i].value = new_val;
   }else{
    elements[i].innerHTML = new_val;
   }
  }
 });

 return pubSub;
}

The model can keep it all the time with diligent your example. In addition to calling the trigger method of jQuery in the setter, it needs to be implemented by calling a custom PubSub publish method:

//In the model's setter
function User(uid){
//...

user = {
//...
set: function(attr_name,val){
 [attr_name] = val;
 //Use the "publish" method (uid+ ":change", attr_name, val,this);
  }
 }

 //...
}  

Again, we achieved the same result using native JavaScript code instead of using a bloated JavaScript framework.

This article is translated from easy two way data-binding in JavaScript, original address/blog/2012/12/02/easy-two-way-data-binding-in-javascript/

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.