Binding behavior, you’re doing it wrong

Date

As a fellow web dev you’re probably separating your HTML and JavaScript layers following the holy peanut strategy we all love. This separation is classically done based on CSS classes. In this article I’ll compare this classic bind-on-class-name method with the new and improved bind-on-data-attribute method.

Let’s take a look at the classic class name example.

<div class="js-foo"></div>

<div class="js-bar"></div>

<div class="js-baz"></div>

<script src="Foo.js"></script>
<script src="Bar.js"></script>
<script src="Baz.js"></script>

The Foo, Bar and Baz files all register their JavaScript classes with the same name on the global scope. So for “Foo.js” this would be window.Foo.

Alright, let’s look at the initialisation logic.

// load foos
var nodes = document.getElementsByClassname('js-foo');
var l = nodes.length;
for (var i=0; i<l; i++) {
    new Foo(nodes[i]);
}

// load bars
nodes = document.getElementsByClassname('js-bar');
l = nodes.length;
for (i=0; i<l; i++) {
    new Bar(nodes[i]);
}

// load bazzes
nodes = document.getElementsByClassname('js-baz');
l = nodes.length;
for (i=0; i<l; i++) {
    new Baz(nodes[i]);
}

In the example, we’re binding our JavaScript using CSS classes. By prefixing those with js- we’re making clear to other developers that these classes are JavaScript related.

Our initialisation script queries the DOM for each available class name. The elements in the returned nodelists are then used to initialise the various JavaScript modules.

Works fine right? Well, it does, but, using the above setup, your logic needs to know the names of each CSS class and each and everyone of the JavaScript modules.

Let’s give this a shot using data attributes and examine the differences.

<div data-module="Foo"></div>

<div data-module="Bar"></div>

<div data-module="Baz"></div>

<script src="Foo.js"></script>
<script src="Bar.js"></script>
<script src="Baz.js"></script>
var nodes = document.querySelectorAll('[data-module]');
var l = nodes.length;
var node;
var name;
for (var i=0; i<l; i++) {
    node = nodes[i];
    name = node.getAttribute('data-module');
    new window[name](node);
}

We fetch all nodes matching having a data-module attribute. While looping over those nodes we get the name of the module from the data-module attribute itself and then reference it on the global scope (window[name]). Done. Single loop.

While our classic getElementsByClassName method performs a little bit better than the querySelectorAll method the later unlocks advantages that outweigh this performance difference.

The above is a stepping stone. Because of a better separation of concerns you’ll force yourself to think more modular. This results in cleaner, more reusable and maintainable code.

Give data attributes a go, you’ll never look back!

Rik Schennink

Web enthusiast

@rikschennink