Refactoring in jQuery: ID-happiness to code happiness

Most jQuery advocates will agree that the single most powerful component of jQuery is its ability to query elements from the DOM. Power means freedom. It means you can do how you please. But, with anything that powerful, there’s a flipside to the story. It’s too easy to write unwieldy jQuery code without some basic constraints. That’s where we need to think of some simple conventions to make sure we are painting pretty pictures with our code.
Whenever I need to do something substantial in jQuery, I usually start by getting too “ID-happy.” The thought-process is simple and lazy: I want to make this button change this element when you click it. Simple. Let’s give it a unique ID and then write a quick click handler for it!
$(‘#my_unique_button_28’).click(function() {
/*Make something happen!
I can write this kind of code quickly, throw it at the end of my quickly expanding $(document).ready()function and move on. I can get something running and working on the order of minutes.
But, this is where the freedom can hurt. Do this enough and you’re left with a host of event handlers applied to elements without giving much thought to what or who or why this page behaves the way it does. In the end, you’re only looking at the trees and not the forest. You end up with what amounts to a laundry list of “things-just-happening” without seeing the greater connection.
$(‘#my_unique_button_28’).click(function() { doThingA(); }
$(‘#my_unique_button_29’).click(function() { doThingB(); }
$(‘li a’).click(function() { doThingA(); doThingC(); }
$(“.some_random_class”).click(function() { doThingD(); }
But, converting this chaotic mess into something more elegant is also very easy. That’s what really makes jQuery so powerful. I can take a “dust-settling” approach after I get things to work. That’s where you need to apply your own mental framework on top of what jQuery gives you.
IDs vs. Classes
Because I tend to be ID-happy, my jQuery code ends up being a laundry list of ID-specific events. I’ll notice that a ton of almost-the-same functional code lives in these various event handlers, and, like I’d do in any programming language, I find an opportunity to refactor. Take the common bits out into its own method, pass the differences in as parameters, and shrink your code accordingly.
But, I’m still left with a bunch of events on too many IDs doing the same things. That’s where I can take advantage of the polymorphic nature of jQuery – instead of tying similar functions to IDs, I can move them to classes.
When you get in the habit of doing this, the forest starts to come into view. You’ll find that many elements on your page have some common set of behaviors. This is functionality that should be represented by a class rather than an ID. You’ll also find lots of behavior that only applies to one specific element – that’s functionality that should be represented by an ID, rather than a class.
In jQuery, the class attribute should represent an inherited piece of behavior whereas the ID attribute should represent unique behavior or a unique identifier.
In other words, I find where my ID-happiness is really just ID-abuse, and where it makes sense. It’s then a fairly clean exercise to add classes to the appropriate elements and apply the right event handlers to the right jQuery selectors.
That’s what makes jQuery so elegant – any two elements can share a similar set of behavior and have their own unique set of behavior. The polymorphic nature of jQuery selectors works perfectly with the medium.
A quick example
Let me show you a quick example. I’ll focus solely on the thought-process that led me to the final code, rather than the code itself. In the end, this is a fairly pedestrian example but the process of getting to the forest is what I find valuable.
I recently built a simple panel rotator in jQuery:

The rotator has a series of X-number of divs with a chronologically-ordered naming convention for each div’s ID (e.g. “div_0”, “div_1”, “div_2”, “div_3”, and so forth). Here, the default div is just a picture of a divi-divi tree.*
* – I swear to you, it is only now as I write this that I realize the dual irony of (a) showing a tree after using the “forest-through-the-trees” cliché, and (b) showing a divi-divi tree.
Back to business. I set the CSS display:none for all but the one that should display (I set that to “display:block“). The navigation, where the meat of the jQuery work lives, is at the bottom. There’s a “previous” and “next” arrow along with a line of X-number of circular buttons. Clicking on any of the arrows or buttons swaps out the divs while also changing the selected state of the corresponding circular button.
I start as I normally do. I get ID-happy. Let’s start with the “next” arrow.
Step 1: Apply behavior to the “Next” arrow
I apply an ID to the “next” arrow (“next_btn“) and write a click handler to swap the current div with the next div. In so doing, I end up adding a hidden input field that tracks the current div number.
When a person clicks the arrow, I increment the current div number, check that it exists in the DOM, and, if it does, I hide the current div and show the new div. Then, I set the hidden input field’s value to that new number. Applying the same ID naming convention to the circular buttons, I can show the selected state of the appropriate buttons as well.
Step 2: Apply behavior to the “Previous” arrow
I apply the same process to the “previous” arrow. It gets an ID (“prev_btn“) and a click handler. Knowing I’m doing mainly the same thing, I just copy-and-paste the code for the next arrow over as a start. I quickly see the only real difference is that the div I want to show is found by decrementing the hidden input value.
So, a quick refactor pulls the logic out into its own function (toggleDivs(show)). The function takes in the one differentiator – which parameter to show.
Step 3: Apply behavior to the circular buttons
With the circular buttons, it’s immediately obvious that they all share the same functionality, except that each one represents a different div in the rotating stack. In fact, they apply the exact same behavior to the panels as with the previous and next arrows. The difference is, each specific button shows its selected state when its corresponding div is displayed.
The dust-settles and the picture gets clear
Once we get the code to a working, despite ugly, state, we get to work on refactoring and taking advantage of the loose, polymorphic nature of jQuery selectors.
Taking a closer look, we’ve separated unique identification (ID) from common behavior (class). Take a look again:
All seven buttons have common behavior – they toggle the div stack. But each button individually reacts to the behavior differently. The previous and next arrows aren’t affected at all, while the circular buttons show their selected state if their corresponding div is displayed.
There’s a bit of adjustment to make to the prior statement, though. While all seven buttons have the same common behavior, they each toggle a different div. In my final code, I store the ID of that div in the rel attribute (There are probably more elegant, correct ways of doing this, but it suffices my needs right now). For the circular buttons, the rel value maps directly to their own IDs (e.g. Circle button 2 toggles to div_2 when clicked, and so forth). For the previous and next buttons, their rel values change each time any button is clicked.
I can then pull apart what ought to be represented as a class from what ought to be represented as an ID.
Behavior that should be represented by a class
So, what behavior should use classes? There are three I came up with:
  • All seven buttons share a common behavior – they all toggle the div stack. In my code, I create a toggle class and write a specific event handler for all toggle classes.
  • All five of the circular buttons share a common behavior – they may be affected whenever the div stack changes. I create a circle class. Then, in my click event for the toggle class, I can use $(‘.circle’)to first set all circle elements to their unselected state before finding the specific circle that needs to be selected.
  • And naturally, all five of the circular buttons share another common trait – they may be in a selected state. I create a selected class and, in my click event for the toggle class, I can add the selected class to the appropriate circle.
Because I can apply multiple classes to any DOM element, separating behavior into specific classes makes the HTML changes really easy and semantically sensible.
  • Each circular button should have a class of both toggle and circle.
  • The “previous” and “next” buttons should have a class of toggle.
  • Whenever the div stack changes (because an element with a class of toggle is clicked), we should add the selected class to the relevant circular button, using addClass().

Behavior that should be represented by an ID
But each button also needs its own ID. We use the ID of the circular buttons so that jQuery can select the exact button to display as selected.

We also will use the ID of the previous and next buttons so that jQuery can modify their rel values after any button is clicked.
So there we have it. I start off ID-happy, writing functionality without really caring about elegance. Once the dust settles, I decide what behaviors fall under a class construct (because they are used by more than one element) and what behavior falls under an ID construct (because they represent something specific to just one unique element). I then just have to modify the ID and class attributes of the participating DOM elements. The refactoring is quick and easy.
As I mentioned earlier, I purposely didn’t get into the code in great detail here. Instead, download this zip and run it locally. Take a look at index.html for the details, and notice how the IDs and classes are employed after the refactoring.