Implement Two-way Data Binding in JavaScript
One of the fundamental features of modern JavaScript libraries and frameworks
is data binding. The data binding features offered by them allows you to keep
model and view in sync. Did you ever wonder how something similar can be
implemented in plain JavaScript? If so this article shows you one such simple
yet working implementation. Although simple implementation as discussed in this
article is not intended to substitute what is offered by these libraries and
frameworks, it will certainly give you a glimpse of how data binding might be
implemented if needed.
Data binding with an example
Ok. Let's see what we are going to develop. Have a look at the following
figure :

The above figure shows two textboxes and one <div> element displaying a
message - Hello World!. This message is actually comes from the Message property
of a JavaScript object. Whenever you change the value in the textboxes our data
binding implementation pushes the changes to the underlying object. On the same
lines whenever you change the Message property the changes are reflected on the
user interface. This way the Message property and all the three UI elements are
kept in sync.
The HTML markup
The above page uses the following HTML markup:
<input type=text id="textBox1">
<br /><br />
<input type=text id="textBox2">
<br /><br />
<div id="divMessage"></div>
The page consists of two textboxes with IDs textBox1 and textBox2
respectively. There is a <div> with ID of divMessage. You can place this markup
either in an HTML file or inside an ASP.NET Core view file.
The JavaScript code that does the data binding
Before we go ahead and demystify the code that implements the data binding,
let's see the usage of what we are going to develop. That way you will get
better idea of how the feature is being implemented. Consider the following
JavaScript code :
var obj = {};
obj.Message = "Hello World!";
var dataBinder = new DataBinder({
"model": obj,
"property": "Message"
});
dataBinder.addDataBinding("textBox1", "value", "keyup");
dataBinder.addDataBinding("textBox2", "value", "keyup");
dataBinder.addDataBinding("divMessage", "innerHTML");
obj.Message = "Hello Universe!";
The above JavaScript code can be placed inside a <script> block at the bottom
of the HTML file. Let's see what this code is doing.
The code creates a new JavaScript object - obj - and sets its Message
property to "Hello World!".
It then proceeds to create a new DataBinder object. What's DataBinder? It's a
JavaScript function we will create later. The DataBinder function is the heart
of our data binding implementation. If you see the syntax, we are creating a
function object using the new keyword. We also pass another object literal
to the constructor that contains some settings.
The settings object passed in the constructor has two properties :
- model : holds the object on which data binding is to be implemented.
- property : holds the name of the object's property that will participate
in the data binding.
The next three lines define the data bindings between the model object and
the UI elements. The DataBinder object exposes a method - addDataBinding() -
that takes three parameters :
- ID of the UI element that is participating in the data binding.
- DOM property of the UI element that will participate in the data
binding. For textboxes, we want to data bind value property whereas for the
<div> we want to data bind the innerHTML property.
- The third optional parameter is the name of an event that will trigger
synchronization between model data and UI. It is applicable to textboxes
since they are capable of changing their value when user enters data. We use
keyup event for this purpose. On the other hand <div> won't have this
parameter for obvious reason.
Finally, we set the Message property of the model object to - Hello Universe!
(remember that the default value is - Hello World!). This is just to check
whether our data binding works as expected when the page loads in the browser.
Implementation of DataBinder
Add a new JavaScript file under wwwroot folder and write the skeleton of
DataBinder function as shown below :
function DataBinder(settings) {
...
}
The DataBinder function takes a settings object as a parameter.
Now, let's complete this function step-by-step.
Add the following code inside the DataBinder() function :
_this = this;
this.dataBindings = [];
this.value = settings.model[settings.property];
The above stores the "this" reference in a local variable called _this. We do
this so that _this can be used safely inside other code blocks of DataBinder().
Then we declare a dataBindings array. The dataBindings array holds a list of
binding objects. A binding object is basically a pair of an HTML element and its
DOM property that is to be data bound. The dataBindings array is populated by
the addDataBinding() function discussed later.
The code then sets the value member of DataBinder to the value of model's
property. Notice how the model property value is obtained using the dictionary
syntax. In our specific example, model will the obj and property will be
Message.
Next, add the following fragment of code below the above lines :
this.propertyGet = function () {
return _this.value;
};
this.propertySet = function (value) {
_this.value = value;
for (var i = 0; i < _this.dataBindings.length; i++) {
var binding = _this.dataBindings[i];
binding.element[binding.attribute] = value;
}
};
Object.defineProperty(settings.model, settings.property, {
get: this.propertyGet,
set: this.propertySet
});
This is possibly the most important part of DataBinder function. Here, we
define two functions namely propertyGet() and propertySet(). The propertyGet()
function returns the value to the caller. The propertySet() function sets the
value and also sets the DOM property of all the HTML elements participating in
the data binding. This is done by iterating through the dataBindings array and
setting the DOM element's property to value.
JavaScript allows you to specify custom getter and setter functions that are
used when an object's property is accessed. This is done using the
defineProperty() method shown next.
The
Object.defineProperty() takes three parameters:
- The object whose properties need custom getter and setters - obj in this
case.
- The object's property whose access is granted through the custom getter
and setter - Message in this case.
- The custom set and set functions wrapped in an object literal.
Now we will add the addDataBinding() method as shown below.
this.addDataBinding = function (element, property, event) {
var domElement = document.getElementById(element);
var binding = {
element: domElement,
property: property
};
if (event) {
domElement.addEventListener(event, function () {
_this.propertySet(domElement[property]);
});
binding.event = event;
}
this.dataBindings.push(binding);
domElement[property] = _this.value;
return _this;
};
The addDataBinding() method takes three parameters (see previous section).
Inside, we grab the target HTML element using getElementById() and store its
reference in domElement variable.
The code then creates a binding definition object containing two properties
namely element and property. The binding definition object can also have
optional event property. The event property is assigned only for those elements
where an HTML event (such as keyup) is specified while adding a data binding.
That's why we check the event parameter and if present wire an event handler
using the addEventListener() method. The event handler simply calls the custom
property setter by passing the DOM element's value. This way model's property is
synchronized with the UI element's value.
The binding definition object is then pushed to the dataBindings array using
the push() method. Finally, the DOM element's property is synchronized with the
value member. We need to do this so that when a new binding is added the UI
element will reflect the model's property value.
Finally, complete the DataBinder with this line:
settings.model[settings.property] = this.value;
Ok. So far so good.
Now add a <script> reference to the DataBinder.js file page you just created
in the Index.
<script src="DataBinder.js"></script>
Run the Index page and try changing values in the textboxes. If all goes well
you should see all the three DOM elements synchronizing their values. The
following figure shows a sample run of the page:

That's it for now! Keep coding!!