I did a guest lecture Tuesday night at Ignition, a coding school run by Spark Boulder that prepares students for their first coding internship. It was fun and I learned a lot about how to improve lecturing abilities. I also got a lot of good questions and one question that I didn’t think I answered well enough, so I thought I’d elaborate.
If you have no interest in JavaScript, you may want to stop reading now. 🙂
The question:
Should we only select DOM elements by ID in JavaScript? If so, why are you using only classes?
This is a great question.
In most cases, it makes sense to separate JavaScript (JS) selectors from stylesheet selectors. This allow you to change presentation (styles) without breaking behavior (JS), but are IDs the best way to do this? I say no. Below I walk through several reasons why IDs may not be the best solution and I suggest an alternative.
Some Reasons I Don’t Use IDs As JavaScript Selectors
IDs Can Only Be Used One Time On A Page
Let’s say we have a group of repeating elements to which we want to bind a click. With a class it is easy, but with an ID you’d need to iterate the ID name (#element01, #element02, …,#elementN). This isn’t a big deal when using a loop to create the HTML. In the jQuery/JavaScript, however, instead of using a simple selector like $(‘.element’) for a class, you’d need to use a more complex one like $(‘#element01, #element02, …, #elementN’). And if you have a dynamic number of elements? Then you’d need an even more complex selector like $(“[id^=element]”). This selector grabs any element that starts with the ID element.
No big deal. Right? I guess.
The complexity isn’t that bad and you still get the benefit of separating behavior and presentation, but what if you need to target something inside the repeating element. A good example of this is an accordion; you know, a list of items (siblings) that expand on click and collapse when a sibling is clicked. With jQuery, you’d add a click event to an item where [id^=element]. The element that is clicked can be used to find it’s sibling, but if you’re only using IDs, you’d also have to add another cumbersome, iterating ID (e.g. id^=sibling) to the sibling (#sibling01, #sibling02) .
You can probably see where I’m going with this, given complex and dynamic functionality, you’ll find it increasingly difficult to iterate every element that you want to select with JavaScript.
Some people like complexity, but let’s say for some reason you can’t loop the HTML to easily make the IDs unique; for instance, when you’re not using real data. Prepare to waste time. And when the client asks to change the order of the elements? More time gone. And what about when your function stops working all together because you had two identical IDs? Not good. And what about when you accidentally bind #elementary and #elemental when you only thought you were binding #element+number. Yes, another debugging problem.
The fact that you can only use IDs once means that your code is more complex and difficult to maintain.
You Can Only Add One ID Per Element
Let’s say I add some complex animation to an element by ID. Later, I realize I want to add some unrelated analytics to that element as well. I could bind it to the same ID, but that could pose a problem if it also needs to be added to other elements on the same page. With IDs, there is no way I can add another selector to keep these unrelated behaviors separate.
I Like To Write Stuff To Be Repeatable
Let’s say I write a block of code using an ID and then I decide I want to repeat it later. If I used IDs I’d have to copy and paste the HTML, make the IDs unique, and go back to update the JS. Why not just write it to be repeatable the first time? You can’t do that with IDs.
Presentation And Behavior Are Often Interconnected
Good website design often includes changes to style based on the state of an element (open, closed, focused, clicked, etc.). An extremely easy way to do this is to use the jQuery functions .addClass(), .removeClass(), and .hasClass(). By its very nature, this feature requires that we manipulate classes with JS.
Many times, presentation and behavior are interrelated and separating them in some cases isn’t good.
I Like To Make Selectors Easily Searchable In JavaScript
I’ve worked on several big sites. These were built and maintained by many developers (in house and out) and they have a lot of moving parts. When adding and changing functionality, these sites have many files that affect the elements of any page—sometimes files on top of files.
Let’s say I need to make a change to a repeating element. It would be nice to search all files for the element’s selector (#element01) and then easily update; however, remember I had to use an iterating ID and a cumbersome JS selector; therefore, I can’t just search all files for “.element”; instead, I need to search for just “element”. On a big site, I’ll probably find hundreds of pages that contain that string, making it very difficult to find all references to this selector in the JavaScript.
I solve this problem in two ways, (1) I try to make my selector names specific and unique, and (2) I never modify the selector name in JavaScript; for example, if the id=”element01″ I always write the JS selector as #element01, never [id^=element]. This way I can easily search for it and find anything and everything that is bound to that selector. You can’t do that with IDs on repeating elements.
A Proposed Solution
So, what if we all agreed on a solution that gets many of the benefits of using IDs while still solving the problems I introduced above? Would it be adopted? Probably not, but here goes anyways.
I submit that instead of using IDs for JavaScript, we just use classes. Ok, hear me out. If behavior and presentation are interconnected, we attach both CSS and JS to the same selector, but we never alter a selector’s class name in JS to make them easily searchable and update-able. Whenever we make a change to presentation, we just search the whole site for that string and update any JS attached to it.
Whenever possible, we separate behavior and presentation by using a naming convention to identify classes that are only used as JS selectors. For instance, we add .js to the beginning of selectors only used for behavior (e.g. .jsElement, .jsSibling, etc.).
This simple rule would solve all our problems. What do you think?
Rebuttal
I’m sure there are things I didn’t take into account above. I’d love to hear your comments below. In an attempt to rebuttal some of the easy responses to my proposal above, I’ve included a couple thoughts here.
I know someone is thinking, “but classes are slower selectors than IDs.” Yes, but I’d argue it is negligible. You’re still talking milliseconds. I heard someone say once that when you write code, write it as elegant as possible even if it occasionally sacrifices speed. Then, go back and rewrite if only if necessary (although it isn’t most of the time).
And others are thinking, “but data elements as JS selectors would be better.” The problem with data elements is that you’ll always have instances when behavior and presentation are interconnected. Why not just have one simple rule that solves all our problems, rather than using data elements except when we don’t. And data elements are slower. 🙂
What do you think? Comment below.