LinkedInFacebooktwitter

An Introduction to JavaScript Chaining

By Richard Griffiths - Director. 

In this article I am going to demonstrate some simple JavaScript chaining techniques with a useful example of working with Document Object Model (DOM) elements to build a message overlay box. The overlay box we will be building consists of a DIV element containing an H2 element, a paragraph and a button with an event that will close the overlay when clicked. With some simple CSS the overlay will look like this:

Hello World!

Click the button below to close

Close

What is Chaining?

JavaScript Chaining is the process of stringing function calls together in one statement, for example:

f1().f2().f3() ...

Now that might look strange and indeed it wouldn’t work at all without the magic ingredient that enables chaining to occur – each function must return a reference to an object that exposes the next function as a method. Consider the following functions:

function f2() {
    console.log("function 2");
}
function f1() {
    console.log("function 1");
}

To log “function 1” and “function 2” to the console you would need to call:

f1();
f2();

To allow us to chain these 2 functions together, f1 needs to return an object that exposes f2 as a method:

function f2() {
    console.log("function 2");
}
function f1() {
    console.log("function 1");
    return {
        m1: f2
    };
}

In this case f1() returns an object with a method m1 which references f2, allowing us to chain the calls together as:

f1().m1();

Why Chaining?

Chaining allows us to reduce the amount of code we have to write and makes our code more readable. Chaining can often be achieved with very little change to your original code; consider the following person function:

var person = function (f, s, a) {
    this.forename = f;
    this.surname = s
    this.age = a;
 
    this.birthday = function() {
        alert("Happy Birthday " + this.forename);
        this.age += 1;
    }
    this.currentage = function () {
        alert(this.age);
    }
}

We can create Joe as a person, set his birthday and then get his new age as follows:

var joe = new person('Joe', 'Bloggs', 20);
joe.birthday();
joe.currentage();

By adding just one line of code to the birthday method we can implement chaining:

this.birthday = function() {
    alert("Happy Birthday " + this.forename);
    this.age += 1;
    return this;
}

And our birthday and currentage functions can now be chained:

joe.birthday().currentage();

A Real World Example

For me, chaining works best when you need to perform multiple actions on a single object, which is something we do a lot when working with the DOM.

In our overlay example, to create the overlay we first need to create a DIV element, specify a class and append it to the document body:

var overlay = document.createElement("DIV");
overlay.className = "overlay";
document.body.appendChild(overlay);

We then create a H2 element for our heading, set the text and append it to the DIV:

var oh2 = document.createElement("H2");
oh2.appendChild(document.createTextNode("Hello World!"));
overlay.appendChild(oh2);

We then create a paragraph of text and add that to the overlay:

var op = document.createElement("P");
op.appendChild(document.createTextNode("Click the button below to close"));
overlay.appendChild(op);

Then we create a button as an INPUT element with type of button, specify its value and add that to the overlay:

var ob = document.createElement("INPUT");
ob.setAttribute("type", "button");
ob.setAttribute("value", "Close");
overlay.appendChild(ob);

We then need to add an event to the button so that the overlay will be closed when the button is clicked. We also want to handle older versions of IE which use attachEvent instead of addEventListener (checking for older versions is outside the scope of this article so assume that isOldIE is true or false):

if (isOldIE) {
    ob.attachEvent("onclick", function () { document.body.removeChild(overlay); });
}
else {
    ob.addEventListener("click", function () { document.body.removeChild(overlay); }, false);
}

This is a typical example of interacting with the DOM, whereby we create objects and perform multiple operations on them. In this scenario, chaining could really help improve the speed at which we can produce the code and improve the readability of the code.

To implement chaining in this example, we need an object that encapsulates the DOM element we are working with and exposes the methods we need to manipulate that object. We start by creating an object to hold the DOM element and a method that will get an element already in the DOM or create a new one:

var cdom = {
    element: null,
 
    get: function (o) {
        var obj = Object.create(this);
        obj.element = (typeof o == "object") ? o : document.createElement(o);
        return obj;
    }
}

The get method expects one parameter which can either be a current DOM element or the tag name of a new element to be created. The first thing get does is to create a new instance of cdom using Object.create. It then sets the value of element based on the parameter passed and returns the new object instance.

We now need to add another method that will handle appending new elements:

append: function (o) {
    var obj = cdom.get(o);
    this.element.appendChild(obj.element);
    return obj;
}

This method calls cdom.get, to create a new instance of the cdom object, passing the new element tag name as the parameter. It then appends the element from the new object to the element in its own object and returns the new object. A third method will handle setting class names:

css: function (c) {
   this.element.className = c;
   return this;
}

This method receives a string as a parameter and then sets that as the class name of the current element. So let’s see this in action:

var overlay = cdom.get(document.body).append("DIV").css("overlay");

cdom.get(document.body) will create a new instance of cdom and sets the element to be the body element, .append(“DIV”) then appends a DIV element to body and returns the new instance of cdom. The overlay variable is now the instance of cdom that holds the DIV so .css(“overlay”) will set the class of overlay on the new DIV.

We need to store the object holding the DIV as a variable because we now need to append multiple elements to it – the heading, paragraph and button – and to do this we need a few more methods. Firstly, we need to be able to add text to the heading and paragraph elements so we need a text method:

text: function (t) {
    this.element.appendChild(document.createTextNode(t));
    return this;
}

This method receives a string of text and uses it to create and append a text node to the current element. We can now append the heading and paragraph as follows:

overlay.append("H2").text("Hello World!");
overlay.append("P").text("Click the button below to close");

The last element we need is the button and for this we need two more methods, one to specify attributes and one to add events:

attribute: function (k, v) {
    this.element.setAttribute(k, v);
    return this;
},
event: function (e, f) {
    (isOldIE) ? this.element.attachEvent("on" + e, f) : this.element.addEventListener(e, f, false);
    return this;
}

The attribute method expects the key and value parameters for the attribute and sets those on the current element using setAttribute. The event method expects the event type, e.g. click or mouseover, and the function to be called when that event fires. Using these two methods we can now add our button:

overlay.append("INPUT").attribute("type", "button").attribute("value", "Close").event("click", function () { document.body.removeChild(overlay.element); });

Final Code

The full cdom code is:

var cdom = {
    element: null,
 
    get: function (o) {
        var obj = Object.create(this);
        obj.element = (typeof o == "object") ? o : document.createElement(o);
        return obj;
    },
    append: function (o) {
        var obj = cdom.get(o);
        this.element.appendChild(obj.element);
        return obj;
    },
    text: function (t) {
        this.element.appendChild(document.createTextNode(t));
        return this;
    },
    attribute: function (k, v) {
        this.element.setAttribute(k, v);
        return this;
    },
    css: function (c) {
        this.element.classList.add(c);
        return this;
    },
    event: function (e, f) {
        if (isOldIE) {
            this.element.attachEvent("on" + e, f);
        }
        else {
            this.element.addEventListener(e, f, false);
        }
        return this;
    }
}

Using this cdom object, we can now interact with the DOM quickly and our code is much more readable. The four lines of code we need to create our overlay are:

var overlay = cdom.get(document.body).append("DIV").css("overlay");
overlay.append("H2").text("Hello World!");
overlay.append("P").text("Click the button below to close");
overlay.append("INPUT").attribute("type", "button").attribute("value", "Close").event("click", function () { document.body.removeChild(overlay.element); });

View the full code on JSFiddle

Back

You might also like...