Skip to main content

Experience Management

Reusable Components for a CMS, Using the Revealing Module Pattern

Before we get started, I recommend reading about the revealing module pattern and closure, if you’re not already familiar with them.
When you are building components for use in a CMS, it’s important to understand that you have less control over the use of these components than you may initially think. Programming these blocks in such a way that they operate independently and discretely becomes more of an issue than if you were building a static or informational site, where you might be able to exhibit more control over the usage and structure of the components.
A good way to combat this is by making sure that as you’re writing the functionality of all of these blocks, you are mindful of their “scope leak” and “clobbering.” By leveraging JavaScript scope and closure, and keeping best practices, we can be sure that our blocks play nice with others.

Scope Leak

In JavaScript, “scope leak” is the concept of where access to discrete pieces of code get defined globally. Variables become defined globally. Functions are placed on the window object. If this is done with little-to-no regard for future-proofing, the global namespace can get quite messy and unwieldy.
Example

var componentOptions = {...};
console.log(window.componentOptions);

In this example, the object componentOptions was defined globally off of the window, and therefore is accessible from anywhere else in the code base.

Clobbering

When you don’t pay attention to your scoping, and you commit the various crimes of “scope leak,” there’s a chance that the functionality of one component will completely or partially override the functionality of another component.
Example

// Component 1
var componentOptions = {...};
function doSomething() {...}
// Component 2
var componentOptions = {...};
function doSomething() {...}

In this example, both components’ code are defined globally off of window. Since both options objects and both doSomething functions are named the same, whichever component is initialized last will overwrite the first component’s options and function.

Closure

In JavaScript, variables and functions are lexically scoped; when a variable or function is defined inside of a function, they are only available from within that function and any of its “children” functions. While this might be a strange concept for an entry level JS programmer to grasp, the subtle nuances of the language shine brightly in the concept of closure.
Example

function init(){
    var localVar = true;
    console.log(localVar);
}
init();
console.log(localVar);

In this example, localVar is defined within the init function. When logged inside of init, localVar will be true. However, when logged outside of init, localVar will be undefined. This phenomena allows us to use the revealing module pattern to discourage poor practices.

Revealing Module Pattern

When we create a component, if we create its discrete functionality within a function, its code will operate separately from other component code due to closure. In this way, we’re able to build blocks of code that are portable, reusable, and independent of the rest of the code base.
Example

var component = (function(){
    var localVar = true;
    return {
        init: init
    };
    function init(){
        console.log(localVar);
    }
}());
component.init();

In this example, we create a variable and set it equal to an IIFE (immediately invoked functional expression) that returns an object with a single method. The object reveals the init function, which has access to all of the IIFE’s closure (in this case the localVar), and now both the init function and the localVar function are protected against clobbering and are not leaking all over the global scope.

Using the Revealing Module Pattern in a CMS Environment

Here’s the fun part. If all your components are in the revealing module pattern, you have to scope all of your components to somewhere. We recommend namespacing a main container object, that in-turn contains a utils and a components object (with an optional pages object). The issue with this is the global namespace object has to be defined off of window, and it has to be setup before you load any of your component code. Additionally, all your component codes should also guard against null errors.
Example
Script in the <head> tag

var PD = {};

Base script (that executes before any component/util script)

(function(){
    PD = PD || {};
    PD.components = PD.components || {};
    PD.utils = PD.utils || {};
}();

Component script

PD = PD || {};
PD.components = PD.components || {};
PD.components.myComponent = (function(){...}());

In this multi-step example, we first need to define the namespaced object to make sure it’s there for future use. One of the first things we do in our external script files, before we even start defining components, we should write code making sure that the sub-objects exist. We also should write code before defining individual components, all of this is in an effort to avoid some kind of race condition where a component possibly gets defined before the component object, causing a null reference error or accidentally overwriting your defined component with an empty object later. (Note the absence of the var keyword in the second and third parts of this example; in this case, we do want to define the objects globally off of window e.g. window.PD.component).

Initializing Components

When you use the revealing module pattern to create CMS-ready components, you have to come up with a way to initialize them. Traditionally, when you have control over the template or page, you are able to only initialize the components that you actually use on the page before the closing <body> tag. When using a CMS, we are not afforded the ability to know when and where which specific components will be used or in what order or configuration. Therefore, it is important that we make sure each component is given a chance to initialize.
Example
Component script

PD = PD || {};
PD.components = PD.components || {};
PD.components.myComponent = (function(){
    return {
        init: init
    };
    function init(){...}
}());

Base initialization script (after all components/utils have been defined)

(function(){
    var component;
    if (PD.components) {
        for (component in PD.components) {
            if (PD.components.hasOwnProperty(component)
            && PD.components[component]
            && PD.components[component].init) {
                PD.components[component].init();
            }
        }
    }
}());

In this example, the components are defined individually and after all components are defined, the initialization code iterates through all components and initializes them at once. This will work well if both: all the components have a function named “init,” and if every init function checks to see if the component is on the page before attempting to initialize it. For instance, you would not want a gallery to be initialized on every page if there was no gallery on that page to init.
Example

PD = PD || {};
PD.components = PD.components || {};
PD.components.gallery = (function(){
    var $galleries = $();
    return {
        init:init
    };
    function init(){
        $galleries.add($('.my-gallery-selector')); 
        $galleries.each(function(){...});
    }
}());

In this example, the gallery’s initialization code will iterate through all the galleries that have been added to the jQuery collection of $galleries and do something with each of them. If the jQuery collection is empty, nothing will happen.
It is also possible to initialize specific components instead of all components, by calling each component’s init function verbosely.
Example
Base initialization script (after all components/utils have been defined)</>

(function(){
    PD.components.nav.init();
    PD.components.videoModal.init();
    PD.components.capabilities.init();
    ...
}());

In this example, instead of iterating through every component, we choose specific components to initialize in a specific order. There is no immediate downside to this, other than the fact that it is more manual and that initialization calls will have to be added to this list in the future as more components are created. The mass-initializing method works more “automagically.”
TL;DR
Use the revealing module pattern to avoid “scope leak” and “clobbering.” Utilize “closure” to globally define a namespace “container” for your components. Write your components in a way that they can be initialized in any order and irrespective of other components on the page.
 
 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Bryce Taylor

More from this Author

Follow Us
TwitterLinkedinFacebookYoutubeInstagram