I’ve been wanting to write this blog post for some time now. A good deal of thought has been given to this topic and we’ve finally begun implementation of this new methodology into RentPost.

I should preface that there isn’t anything profoundly novel about this approach, and many might even find it a tad “nasty”, I did at first. It goes against what we’ve always thought was the right approach. But after we’ve refactored our js over and over, we’ve found ourselves constantly pushing towards the DOM and increasing cohesion. Keeping an open mind, I’m challenging that approach and freaking loving it so far!

So, what is this magical new approach that you’re sure to scoff at on first glance, but if you keep an open mind, will hopefully see the value? Well, it has to do with decoupling and cohesion. There is an awesome talk put on byPete Hunt, a Facebook engineer and core team member of the React Project, on the subject. I suggest watching this video. A friend actually pointed me to this video when I was explaining this new methodology at which I’d arrived. Clearly I was not the only one on this wavelength. And while it’s a requirement for React’s virtual DOM, I’m suggesting the event binding implementation in a more mainstream sense.

With that all said, let’s dig into specifically what I’m talking about here and discuss a few positive and negatives, etc.

Firstly, what’s the problem we’re solving… Event binding sucks. It’s a real pain. Not only is it annoying to write the first time, it’s even harder to keep straight all of your bindings as an application’s UI morphs. That’s not even mentioning all the bugs that can arise from incorrect selectors, etc. Let’s take an example:

$(“a.add-new”).on(“click”, function(event) {
    //do stuff
});

Now, let’s take a look at what we did here, super simple, but I want to point out something. This is just a simple event binding on the node itself. But, note that the selector specifies the element. This is something I’ve long been a fan of doing out of practice. It’s not the fastest selector, but when refactoring and reading code, it tells you something more about the selector and the resulting code. Here is a further example:

$(“body”).on(“click”, “ul#users > li a.add-new”, function(event) {
    //do stuff
});

In this example, we took the previous and did a couple more things. For one, we delegated the binging to the <body> and expanded the selector to be more clear exactly what it is. Now, again, not fast, no. But, I can see that clearly, it’s an anchor for adding a new user. This is good for a couple reasons. One, as I mentioned, the ability to clearly see the chain and what’s happening. And two, using longer selectors like this allows you to prevent collision among selectors and allows you to keep the DOM clean. You don’t have to have selectors like a.-add-new-users-button-under-left-column-when-enabled. Any time I see a selector like this I cringe ><.

So, what does all this selector jazz have to do with cohesion and the collective DOM, you’re probably asking… Well, it has everything to do with it. The only reason we spent all this time making sure our selector chain was easy to read and follow within our JS is so that we can read the code and know what’s happening more clearly. Sometimes the javascript code itself isn’t enough to fully know. In these cases, you need more contextual clues. Comments do help, but sometimes they can get out of control as well.

Taking a step back from this and thinking more on it, we’re just trying to create more cohesion between our JS and our markup/DOM. Now, this isn’t intentional as much as it’s really a necessity for continual refactoring down the road. When I started giving this more and more thought, I began to ask myself, why we’re even attempting this decoupling in such a way. And this is where I came upon the following approach:

<a onclick="//dostuff" class="add-new">Add New User</a>

So, yea, inline event handlers! You saw that correctly, yes…

This isn’t it though, there is more, but this is the concept. Allow me to expand on it first. Before we had one page ajax web apps, we had standard anchors with uri’s (still can too if you want to watch for url changes), but honestly, most apps don’t and don’t need to. Graceful degredation, a primary benefit, is generally considered unnecessary work since nearly every browser used today supports javascript — a requirement to use most of the internet. So that said, my take on this is nothing more than a progression of the href attr. Work with me here… Where we use to have href=”/path/to/new/page.html”, in most new apps, you no longer need traditional paths. Instead your buttons and anchors are calling functions, which are making ajax requests and pulling in new json for parsing or doing various other functions required by your app. And because of this, I look at the inline event handlers as a progression.

Disclaimer: I’m well aware of what people say and that these handlers were a thing of the 90’s. I don’t care.

So, let’s take this a couple steps further to complete this approach.

First, I should probably talk a bit more about the organization of js code within an application. This is a topic in and of itself, but in short, I highly recommend setting up “Models”, if you will. Take for example the following:

var Model = Model || {};
Model.User = Model.User || {};
//yes, I like underscore.js
_.extend(Model.User, {
    add: function() {
        //do stuff
    }
};

This is just a simple example, but hopefully you get the idea. Think of it a bit like PHP’s PSR-0 for js ☺

The benefits of this approach are pretty far reaching and this is deserving of an article in and of itself, but a requirement for the proper implementation of the suggested approach. So taking that with us, we now have something like this:

<a onclick="Model.User.add()">Add New User</a>

Now, I don’t know about you, but to me, this is pretty awesome. It’s clean and direct, nothing else in the way. I can also pass in arguments as needed which are almost always available within the DOM for actions like “remove”, “edit”, etc.

This is also not only limited to onclick, onsubmit is another great one, but you could use any event handler needed. For something with custom/unusual handlers I’d probably still resort to registering them within the js files.

So, the benefits:

  • No longer the need to store as many data attr’s for ids and other variables that need to be passed to a function when the event is fired. These are simply added to the function call as arguments.
  • Delegation is no longer a requirement, dynamically added DOM nodes would simply have their event handlers included when inserted.
  • You can keep your js code much cleaner without the need for a large portion of event hander registrations.
  • Garbage collection with delegation for dynamically added nodes isn’t a concern since when the node is removed from the DOM, the event handler is as well.
  • No longer do you have to search around when debugging an UI for what is “firing” on a handler. It will be plain as day and you will know where to go to edit/view the code.
  • You are forced to keep it DRY!
And the downsides:
  • If you have thousands of DOM nodes on a given page with associated event handlers, it could be taxing for the browser. I haven’t tested this, so I can’t confirm. I do consider this to be a much more rare scenario and more of a performance enhancement than anything else. But it’s worth mentioning. You can always just delegate in this case, no big deal.
  • You’re putting your logic in the global scope with the “Model” approach. However, this can be a benefit as much as a negative. It’s nice to be able to call an “action” from anywhere it’s needed without having to build the prototype.
These are honestly the only downsides I’ve been able to come up with to date. I’d love to hear more if anyone has them. Please share your thoughts on this.

I’m certain I’m not the first to take this approach, it’s nothing super novel. But it’s been the result of years of refactoring and improvements that have led me to this conclusion. There was always an aching to get closer and closer to the DOM with the js. Your markup and your js will always be tightly coupled, there isn’t any way around it and the separation of concerns simply isn’t currently feasible. For this reason, I prefer to accept it instead of fight it, and I’m loving it!

P.S. — I’m aware of the many new frameworks that address these concerns and others. However, when refactoring, implementing a framework doesnt’ always make sense. Also, this approach is very simple and achieves a very nice result in itself, without the need to be dependent on a framework.