node package manager

module-js

An extendable class for Web Elements, modules and components

Module JS

This library allows you standardize each module-like Element throughout your single-page web application. A module can essentially be anything, but it usually represents an independent component (like a modal, or a carousel for example).

The idea behind this library is to provide a common interface between all the modules you may want to use on your site. Which all means less code, less work, and less things to unit test.

You can install as an npm package if using a build system like Browserify.

npm install module-js --save-dev

NOTE: Node v7 is also recommended if using this package as an NPM module.

Each module that you have on your site/app will generally be tied to an HTML/DOM element on the page, which you pass to the Module class to create an instance. Then you'll have access to all the helpful methods below. For our example, we'll use an element that represents our modal container (assuming there is already an element in the DOM with the id of "modal-container").

 let modalContainer = document.getElementById('modal-container');
 let modalModule = new Module(modalContainer, {
    styles: 'http://path/to/modal/styles.css',
    template: 'http://path/to/modal/template.hbs',
    data: 'http://path/to/modal/api/data',
    loadedClass: 'modal-loaded'
 });
 

Once your instance is created, you can call the built-in methods on it. For instance, calling load() on the Modal instance (demonstrated below) will load module along with its template, styles, and even fetch the module's data using the options we've specified above. Once loaded, it will also add the loadedClass specified to the module's modalContainer element.

 // load the module 
 modalInstance.load();

NOTE: since a handlebars file was used as the template option above, calling load will inject the data JSON response from the `data`` url specified into the handlebars file.

Out of the box, the Module class will provide a few helpful methods. But the real power comes when you begin to have your components extend it with their own custom implementations. From there, you can override the default abstract methods to add logic that is custom to each specific module. Here is an example of how to create a Modal class that extends the module class and has custom loading functionality.

class MyCustomModule extends Module {
 load () {
     // do some custom stuff before load 
     return super.load().then(() => {
         // do some custom stuff after load 
     });
 }
}

As you can see above, you can overridethe load method of the parent Module class and call it whenever you want. We recommend that you call the Module class's method via super when overriding any of the methods to ensure you get all internal functionality.

When instantiating a module, you can pass it options:

let modalContainer = document.getElementById('modal-container');
let modalModule = new Module(modalContainer, {
    styles: 'http://path/to/modal/styles.css',
    template: 'http://path/to/modal/template.hbs',
    data: 'http://path/to/modal/api/data',
    loadedClass: 'modal-loaded',
    activeClass: 'modal-shown',
    disabledClass: 'modal-disabled',
    errorClass: 'modal-has-error'
});
Option Type Description
styles Array/String Array of stylesheet urls to be loaded (or single url string)
template String HTMLElement
data Object/String The url to the data or the data object that should be loaded into the module's template
requestOptions Object The set of request options that will be passed to the fetch call when fetching data
loadedClass String The CSS class that will be applied to the module element when it is loaded
activeClass String The CSS class that will be applied to the module element when it is shown
disabledClass String The CSS class that will be applied to the module element when it is disabled
errorClass String The CSS class that will be applied to the module when error occurs
onLoad Function A function that fires when module is loaded
onShow Function A function that fires when module is shown
onHide Function A function that fires when module is hidden
onEnable Function A function that fires when module is enabled
onDisable Function A function that fires when module is disabled

Your module will get some useful methods whenever an instance of it is created.

Your module instance will have a .load() method when it is instantiated. When called, it will load all templates, styles, and data passed into the options above, apply your module's loadedClass and, when done, will call your onLoad callback option (if specified).

You can also do some custom load handling tasks. Below you can see how a Carousel class can be created that loads some assets asynchronously when the load() method is called.

class Carousel extends Module {
   load () {
       // code that loads carousel images asynchronously here 
       // and return a promise when done 
       return super.load();
   }
};
let carousel = new Carousel();
 
// trigger carousel load 
carousel.load();
 

Let's say you have the problem (that many of us have) where you need to detect the completion of your module element's CSS transition in javascript before your code can continue. Given the following CSS...

.animate {
    transition-property: background-color;
    transition-duration: 100ms;
    transition-timing-function: ease-out;
}

You can call the waitForTransition method to wait until your Module finishes its transition before doing other things in your javascript code. Like so:

var modal = new Module(document.getElementByTagName('div')[0]);
modal.classList.add('animate'); // start transition 
modal.waitForTransition().then(() => {
    // 100 milliseconds later... 
    console.log('transition complete!');
});

The show() method can be called when you want to set your module to its "active" state. It adds the css activeClass to your Module element. If you have css that transitions your element when the activeClass is applied, you can utilize the returned promise to wait until after the element has transitioned.

let myModule = new Module(document.getElementById('my-module'));
myModule.show().then(function () {
    // module has finished animating into its active state! 
})

The hide() method can be called when you want to set your module to its "inactive" state, which essentially just removes the activeClass. If there any css transitions that take place when the activeClass is removed, you can use the promise it returns to wait until the animation completes.

let myModule = new Module(document.getElementById('my-module'));
myModule.hide().then(function () {
    // module has finished animating into its inactive state! 
})

A method that fetches the data for the Module using the API url supplied as the data option.

let mod = new Module(document.getElementById('my-module'), {
  data: 'path/to/mydata'
});
mod.fetchData().then(function (data) {
    // data has been fetched 
})

Loads the css stylesheets supplied via the styles option.

let mod = new Module(document.getElementById('my-module'), {
  styles: ['path/to/my/style.css', 'path/to/my/second/style.css']
});
mod.getStyles().then(function () {
    // styles have been loaded into the head of the current document 
})

Loads the template supplied via the template option. Supports .html and Handlebars (.hbs) files.

let mod = new Module(document.getElementById('my-module'), {
  template: 'path/to/template.hbs'
});
// you can optionally pass data to be injected into the template 
let data = {my: 'value'};
 
mod.getTemplate(data).then(function (html) {
    // the data has been injected in returned html string 
    this.el.textContent = html;
})

Traverses upwards from the target until finding an ancestor element with the cssClass provided. The target is optional and, if not passed, the Module instance el is used.

The disable() method can be called to add the css disabledClass to your Module element and set your module to a state in which it can no longer be interacted with.

let myModule = new Module(document.getElementById('my-module'));
myModule.disable();

The enable() method does the opposite of the disable() method above, removing the disabledClass from your Module element.

let myModule = new Module(document.getElementById('my-module'));
myModule.enable();

The error() method can be called when you want to manually trigger an error in your module, which adds the errorClass to your Module element.

NOTE: the error() method is called automatically when the load() call on your module fails.

 let myModule = new Module(document.getElementById('my-module'));
 myModule.error();

A property that you can query in order to find out where in the loading process a module is. This will return a string representing one of three states: notLoaded, loading, loaded.

Each module instance provides a subModules object to which you can attach additional modules that have extended the Module class. So when load() or destroy() is called on the parent module, the same methods will be executed for all of the subModules you've specified, saving you from having to call methods on all of the subModules directly. Take a look at the example below.

class MyCustomModule extends Module {
    constructor () {
        // calling super gives you access to the subModules object 
        super();
        this.subModules.myModuleId = new Module();
        this.subModules.myModuleId2 = new Module();
        this.subModules.myModuleId3 = new Module();
 
    }
 };
 let customMod = new MyCustomModule();
 
 // destroy parent module, which will 
 // call destroy on all submodules 
 customMod.destroy();