I recently had the pleasure of presenting at Bethel Middle School’ yearly career day. It was the same fare as last year: explain front-end and back-end programming, describe my own job, show some examples, deal with students that were either too loud or too quiet, and make cryptic remarks how my sister was likely their English teacher.

Something I made specifically for this was a tic-tac-toe game written with JavaScript. My goal was more than the opponent randomly selecting open squares - it actively tries to block the player’s wins and take its own. We played a couple rounds, I explained some of the code, showed them said code, got uncomprehending stares, felt a bizarre satisfaction, and repeated.

In this post I thought I’d explain the general structure of the game’s code. I could explain the specifics, but that’d take away the fun of discovering it yourself, wouldn’t it? But I will write how, despite the complexity, I kept it maintainable, organized, and DRY.

Using Containerized JS

Containerized JS is a pattern a past colleague taught me to keep JS functionality from leaking out. While working on a Pattern Library, we had issues where one component’s JS was inadvertently affecting others. This happened even more with multiple component copies on that page, and click events hit each one in the DOM.

This pattern fixes that by limiting all JS to the DOM within that component, “containing” it to stop leaks. The basic structure I use is below:

function containerized_function(patternId) {
    let pattern = $("#" + patternId);

    // Consistent component variables
    const v = {};

    // Reusable functions, used frequently the component
    const f = {};

    // What to do when the page loads
    function init() {}

    // Events triggered by the user, mostly click events
    function setEvents() {}

    // Running the initial and event functions when page loads
    function docReady() {
        init();
        setEvents();
    }

    $(document).on({ ready: docReady() });
}

$(".component").each(function() {
    var id = $(this).attr("id");

    if (typeof id === typeof undefined && id !== false) {
        id = "IDUNIQUE_" + Math.floor(Math.random() * 999999999999 + 1);
        $(this).attr("id", id);
    }

    containerized_function(id);
});

First is the “containerized function” - that’s where the most important stuff happens. There’s a few important parts to be aware of:

After that, that small bit of code below does the rest. It scans the page, finds every components for the function, gives them unique IDs (if they need them), and runs the function.

There you go! Each component has that functionality contained in its own DOM. Even with several on one page, the functions and variables are independent of each other.

There’s some obvious benefits to all this:

exports.component_init = function(selector, component_function) {
  $(selector).each(function(){

    var id = $(this).attr('id');
    
    if ( typeof id === typeof undefined && id !== false ) {
      
      id = 'IDUNIQUE_' + Math.floor((Math.random() * 99999999999999999) + 1);;
      $(this).attr('id', id);
    }
    
    component_function(id);
  });
}

In the JS file with the containerized function, it can then export the function:

let f = require('./functions');

function containerized_function(patternId) {
    // Function stuff goes here!
}

exports.activate = function(selector) {
  f.component_init(selector, containerized_function);
}

Finally, you’d require and use it in your main file with the selector:

let component = require('./component');

component.activate('.component-class');

There it is - easy, portable, and customizable.

Containerizing the Game

The final result using this pattern worked nicely and is on CodePen. Here’s a broad overview of what’s in each part of the containerization:

The final game is below - scroll through the JS and play a round or two!

See the Pen Tic Tac Toe by Maxwell Antonucci (@max1128) on CodePen.

In Conclusion

This containerization pattern didn’t make the code for key game functions any easier to figure out. Having the computer play smarter than some random square selection still gave plenty of puzzles to solve and walls to bang my fist on. But it did give an easier structure for laying out all the different puzzle pieces and putting them together.

So if you’re coding in a modular environment, especially something like a pattern library, and need a reliable JS component pattern, this one’s definitely worth a try!

~ Cheers, Max A