
Modularized Javascript with ES6 Classes
Modularized javascript classes are a great way to scope functionality to a particular snippet of code. We can also reuse this class if that snippet gets repeated multiple times on the same page. Each snippet or module would essentially have its own scope and set of functions.
Let's start off with a simple counter module. You press a button and a count is incremented or decremented. Here is our basic markup.
<div class="counter" data-module="counter">
<button js-decrement>Decrement value</button>
<button js-increment>Increment value</button>
</div>
Nothing incredibly fancy. Just two buttons that will change the value of our variable. Notice the `data-module` attribute. This will allow to scope logic and functionality to this snippet.
Here is the corresponding class we are going to use.
class Counter {
constructor(domElement) {
this.el = domElement;
this.count = 0;
this.incrementButton = this.el.querySelector('[js-increment]');
this.decrementButton = this.el.querySelector('[js-decrement]');
this.incrementButton.addEventListener('click', this.incrementValue.bind(this));
this.decrementButton.addEventListener('click', this.decrementValue.bind(this));
}
incrementValue() {
this.count += 1;
console.log(this.count);
}
decrementValue() {
this.count -= 1;
console.log(this.count);
}
}
In our example `domElement` will be the parent element that we pass into the constructor of the class. From where we queryselect from the parent element to find only the elements nested within this markup. This allows us to have multiple instances of the same class.
But how does .this class get instantiated? Well we need to create a function that will instantiate a new class of `Counter` for each time we see a `data-module="counter"`...Let's create a register widget function.
const onLoad = (cb) => {
if (document.readyState === 'complete' || document.readyState === 'loaded') {
cb();
} else {
window.addEventListener('DOMContentLoaded', cb);
}
}
const each = (nodeList, cb) => {
if (!nodeList || !nodeList.length) {
return;
}
for (let i = 0; i < nodeList.length; i++) {
cb(nodeList[i], i);
}
}
const registerWidget = (selector, ComponentClass) => {
onLoad(() => {
const domNodes = document.body.querySelectorAll(`[data-module="${selector}"]`);
if (!domNodes.length) {
return;
}
/** Init */
setTimeout(() => {
each(domNodes, domElement => new ComponentClass(domElement));
});
});
};
export default registerWidget;
This javascript looks for elements with a certain data-module attribute followed by the name of the module. In this example the name of the module is `counter`. The code finds all instances of counter and then instantiates a new class of Counter for each HTML element with an attribute of `data-module="counter"`. There are a few utility functions. There is also an `onLoad` function that will only invoke it once the DOM is loaded. The `each` function will iterate over a nodelist with a callback.
Now all that's left is to just call this function on our class, and it will automatically be instantiated when the HTML for this class exists on the page.
import registerWidget from './register-widget';
class Counter {
constructor(domElement) {
this.el = domElement;
this.count = 0;
this.incrementButton = this.el.querySelector('[js-increment]');
this.decrementButton = this.el.querySelector('[js-decrement]');
this.incrementButton.addEventListener('click', this.incrementValue.bind(this));
this.decrementButton.addEventListener('click', this.decrementValue.bind(this));
}
incrementValue() {
this.count += 1;
console.log(this.count);
}
decrementValue() {
this.count -= 1;
console.log(this.count);
}
}
registerWidget('counter', Counter);
On the last line we call the register function that we created earlier and pass in the Class of counter followed by the selector. After that, we have achieved modular javascript classes.

