3
\$\begingroup\$

I have a script for a web page that has to initialize a lot of complex event handlers on page load. Because of the large number of events and DOM elements that these events should fire for, I want to construct a global object containing all the initialization logic.

var Global =
    {
        button1Handler: function () {
            return {
                button1: $("#button1"),
                init: function () {
                    this.button1.on("click", function () { alert("button1 clicked"); });
                }
            };
        },
        button2Handler: function () { /* */},
        init: function () {
            this.button1Handler().init();
            this.button2Handler().init();
            // ...
        }

    };

$(function () {
    Global.init();
});

But as I'm quite new to JavaScript, I have no idea whether this is the most simple or correct way to do this. And I'm not sure I like these chain function calls: button1Handler().init().

Is there a better way to write this piece of code?

\$\endgroup\$
2
\$\begingroup\$

First, I would suggest against the Global name. Too generic. Next, I would suggest against putting it in the global scope. You can put the declaration inside jQuery's ready handler.

Another thing is to avoid the unnecessary "configuration". Sometimes, it's best if the code is straight-forward. The structure of a configuration is sometimes a roadblock. Takes up too much syntax but doesn't immediately tell you what the code does.

For instance, if I was looking at this code for the very first time, I would be wondering how button1 is related to this.button1, what button1 should be, when is init is called, etc. Once I get the hang of it, sure it works. But what if there's an architectural change that invalidates this style of configuration? What if you've done this to 1000 things? You can't easily pull out and rewrite the code.

Keep it simple.

$(function () {

  $("#button1").on('click', handler);
  $("#button2").on('click', handler);

});

On the other hand, if you have an app that is already starting to look complex, consider using a simple framework. There's Ractive, Vue and Riot which are small frameworks allow you to write self-contained portions of the site. This way, each "component" only houses functionality of that component, splitting up this "large number" into sections.

\$\endgroup\$
0
\$\begingroup\$

First you do not need the chain of functions:

button1Handler: {
    button: $("#button1"),
    init: function () {
        this.button.on("click", function () { alert("button1 clicked"); });
    }
},

Next to make it easier to init everything, you could loop through your Global calling each one of the init functions.

var Global = { /* ... */ };

Global.init = function init() {
    for(var elem in Global)
        Global[elem].init();
}

To simplify that you could change Global into an array and remove the button1Handler names.

var Global = [ { button: /* ... */, init: /* ... */ }, /* ... */ ];

$(function() {
    for(var i = Global.length; i--;)
        Global[i].init();
});

Another thing would be to change the name from Global to something more descriptive to its purpose. Something maybe along the lines of Initializer.


If you want to make it a little easier on you, you could make some kind of "class" that builds these mini-initializers for you.

function ElementInitializer(selector, init, handler) {
    this.selector = selector;
    this.$element = $(selector);
    this.init = init;
    this.handler = handler;
}

ElementInitializer.baseInit = function() {
    this.$element.on("click", this.handler);
}

var Initializer = [];

Using it:

Initializer.push(new ElementInitializer("#button1", ElementInitializer.baseInit, function() { alert("button1 clicked"); }));

I will say though that it seems a little unnecessary to have all of this. I would only do that if you have a lot of complicated initialization stuff each element. If you only have a button with a simple event and all you have that is complicated is the handler, then I would let the handler be the complicated part not the rest of my code.

function handler1() { alert("button1 click"); }
function handler2() { alert("button2 click"); }

$(function() {
    $("#button1").on("click", handler1);
    $("#button2").on("click", handler2);
});

Hope this helps!

\$\endgroup\$
0
\$\begingroup\$

For a beginner you have a really good starting point; better than most I've seen. In principle you are following sort of revealing module pattern; not quite right, but it points in the right direction.

Some comments on your code:

1) var Global = if you are the only developer responsible for your JS -or as in my case: make up an example- you are free to name your variables. But if you don't, please choose meaningful names (e.g. taxCalculator, `netflixPlayer etc)

2) You've chosen to encapsulate functionality in a single object. That is good! But I would prefer to make the object's interna less accessible: There is the IIFE-Pattern, which does exactly that. As you know, functions have their own scope. The trick is to immediatey invoke a function (hence the name) function(){}() - notice the second parens () after the definition of the anonymous function. If your function returns an object (like your code did), you have the exact same functionality as in your code above, but made the interna not accessible from extern; you only reveal a well defined API (hence the name revealing module pattern.

As with ordinary functions, you are able to pass parameters (which could in turn be other objects). This allows for a really modular code:

var module1 = function(){
    function init(){
    /* init module */
    console.log("init module!");
  }
  return {
    init:init
  }
}()

var app = function(module1){

    function init(){
    console.log("init app!");
    module1.init();
  }

  return {
    init:init
  }
}(module1)

app.init()

Here is a Fiddle to play with.

This should do for many applications, you write.

If you have many modules, dependencies, etc. I would take a look at RequireJs

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.