48 comments

[ 5.2 ms ] story [ 91.8 ms ] thread
the performance difference between classes and IDs is irrelevant

Instead of a test with a list of 1000 elements, try a test with a nested list of 1000 elements. The performance problem with classes is that the browser has to traverse the DOM to find them, starting from the deepest element and working up. It's pretty quick, but not that quick when you have a deep tree. Whereas finding an element by ID is literally just a single lookup in an index regardless of where the element actually appears.

That said, DOM traversal was a very slow procedure for a long time, so browser developers spent a lot of time and energy optimising it. It's way better than it was. It could well be the case that classes don't have the performance problems they once did. Unfortunately, the test in this article won't show whether that's true or not.

To clarify, if you have a selector like "div.class a", and HTML like;

<div class="class"><div><div><div><div><div><div><a>Woo!</a></div></div></div></div></div></div></div>

..then the browser goes to every anchor in the DOM and then looks up the tree asking 'Does this div have a class of 'class'?". That's what's slow. If you use "div#id a" then the browser can look for the id more quickly. That's why IDs are faster.

Did you read the article? It said explicitly that regardless of `div.class a` or `div#id a` the same lookup takes place - every anchor on the page's parent is checked to see if it has the id or class specified by the selector.

So the selector `a#id` will be faster than `a.class` but a parent selector uses the same heuristic.

(comment deleted)
> The performance problem with classes is that the browser has to traverse the DOM to find them, starting from the deepest element and working up. It's pretty quick, but not that quick when you have a deep tree. Whereas finding an element by ID is literally just a single lookup in an index regardless of where the element actually appears.

Best I'm aware this was true until browsers all supported getElementsByClassName:

http://caniuse.com/#feat=getelementsbyclassname

The only material difference between that and looking up a DOM ID is that the first yields an array (of a potentially wide range of tags) instead of a single element.

Plus, as already highlighted in a separate comment, `div.class a` or `div#id a` will both work the DOM tree from every `a` element up (just like describe) anyway.

Just to pile on what you said, I believe that with Flex and other CSS rules, the DOM tree is less deep then it used to be (less wrappers for alignments).

Obviously, I don't work at the scale of Facebook, but it's a tendency that I have witnessed in different projects.

So it's possible that the speed tradeoff between classes and ids become irrelevant, if it's not already.

(comment deleted)
No, browsers key IDs off a hash table in exactly the same way that they key classes. The reason is that the Web depends on being able to have multiple elements with one ID.

Source: I implemented this in Servo and studied Gecko, WebKit, and Blink code for it.

[Slightly off topic] One of the reasons I wouldn't use ID's is because the DOM element with that ID is automatically introduced to the global scope. Open up the console and type "up_8630368" and you will see it yields a DOM Element.
It's the DOM element to upvote this whole post at the top of the page.

I agree that polluting JavaScript's global space is bad. I wonder if this could be a vector for some attack. Probably not because it would be well known for a long while (but I didn't know about it untile 5 minutes ago, thanks).

It seems like you'd need to inject malicious Javascript into the page before you could run an attack like this. General XSS attacks would be the larger, encompassing vulnerability.
It seems malicious HTML could do it, too, if the global element variable replaces some other global object of the same name.
I get that being annoying, but is it really that big of a deal?
I don't think it is a big deal. It is just slightly arbitrary, in my opinion.
In my personal experience, yes. I first found out about it when it broke something. I was using fineuploader, since there was only one element which used fileuploader, it was #fineuploader, and since fineuploader wasn't an AMD module, it exported window.fineuploader() which got blatted by the element.
Is this true? If I have a element with id="foo", then "foo" will be available as a variable in the global JS context? I tried, but can't reproduce it (Chrome, Windows & Safari, OS X).
OK, sorry about the reply to myself, but it is true indeed.

I was testing with an id which contains a "-" (hyphen). As the hyphen isn't valid for a variable, you can't access it directly or via the window object (with the "dot" notation). However, it is there, and you can access it with the array-like syntax.

Is this part of the spec? Or creative interpretations?

tl;dr: HTML element with id="foo-bar". You can't access it as the variable foo-bar in the global context or window.foo-bar (invalid var name). However, you can access it via window['foo-bar'].

I've been told it's unspecified, but IE implemented it. and when when IE was the most popular browser, other browsers replicated the behaviour as some sites were using it in their JS - as a kind of quick shortcut for document.getElementByID. Don't have a reference though.
That's per spec. Because the global object can be accessed as window (and several other things…), these global "variables" are therefore merely properties on the window object. It's worthwhile to point out that foo["bar"] and foo.bar are entirely equivalent in ES5 — just the former syntax allows more properties to be expressed as it allows an arbitrary string.
It started as creative interpretations, then got added to every browser except Firefox , then got added to Firefox because too many websites were relying on it. And now it's in the spec, because too many sites are relying on it, so a browser that doesn't do it wouldn't be web-compatible.

Classic race to the bottom. :(

Wow, today I learned. I've seen iffy situations with name attributes in IE before, I had no idea that IDs became globals... I can't wait for it to bite me
HN title should be "Don't use IDs in CSS selectors? [2011]"

(missing question mark from original title and the year written)

This is rather like famous "Go To Statement Considered Harmful" dictum of lore, and its variants in other contexts: the key takeaway shouldn't be that we should never use these constructs -- rather than we should be circumspect about using them, as they tend to have hidden side effects.
Totally agree, but in this case I can say for sure that the benefits of using IDs are so impractical, that with no doubt I will add a class name to the target element style the class not the ID.

In my development practice I've found that messing CSS with IDs don't help scale your app ( you can hardly override them ), they are also subject of all the reasons stated here and in the article ( bad performance, global scope introduction, ... ).

The only reason to style IDs would be if I happen to have an old theme that people styling it were using IDs.

I totally don't support styling IDs ( or using !important - which basically is on the same page ).

I don't think you can really make a blanket statement like this. When you make blanket statements, you're ultimately basing a decision on superstition, rather than need or evidence. It's why I don't really care for anything that claims to be "best practices". The term "best practices" really just means "this worked for us in our situation". That's great, and one should be aware of such things, but that doesn't make it a standard or a law.
I disagree with your statement. I believe that "best practices" means that "this is probably how it should be in the majority of uses." Obviously each situation is different and not everyone's solution will work for you, but I'd be willing to be that more often than not, it would.

Whenever something deviates from best practices, I would think that that raises a red flag for justification of difference.

Yeah, but justify to whom? To the Jr. programmer trying to move up in the company, no matter who he has to throw under the bus to get there? To the entrenched Sr. programmer who uses effort to make up for a lack of skill? To the manager who doesn't know anything about programming?
You're mixing politics with good engineering.

I would argue that a quorum of engineers would generally agree with whatever is justifiably deviating from best practices. If it makes sense to you because of good engineering reasons, then it should probably make sense to other engineers. Reality perhaps dictates otherwise, but that's what it should strive for.

> The term "best practices" really just means "this worked for us in our situation"

I strongly disagree. Obviously there are always exceptional circumstances, but if something is really a best practice, that means "if you decide not to do it this way, you should have a good reason for that decision".

Now I would agree that people are too quick to call something a "best practice", when really it's just their personal preference (e.g. "always use semicolons in JS", or "never use semicolons in JS").

Well, that's really what I mean. Too many people call their personal habits "best practices", to the point that I just can't be bothered to listen to what the general population of programmers has to say about how I do my own job.
Exactly. If you are going to call something "best practice" then first you need to put it in a formal document, and second, that document should be signed off by a wide panel of experts from a broad cross-section of the relevant industry.

The coding style guide put out by your 12 member startup isn't "best practice" for everyone. It's just best practice for continuing to work there.

I have to admit to not feeling too strongly either way about this, but the argument I would have against the 'never use IDs in CSS selectors' is purely a semantic one.

An ID, by design, semantically references a single thing. This has important meaning when making HTML (amd CSS) readable and parseable. Now, I'm not necessarily saying this is a 'winning' argument, but throwing away a useful tool for declaring semantic meaning for hand wave'y arguments about 'britality' feels like overkill.

That's exactly the problem - you're referencing a single element which can't be reused. I bet all the styling being used on the element could be broken down in to reusable classes leading to cleaner code and less specificity.
I would argue about cleaner code, in my view doing something like:

    // HTML
    <div id="myLoginWidget">...</div>
    
    // SCSS
    #myLoginWidget {
      @extends #baseWidget;
      @extends #somethingElse;
      @include rounded-cornders(0.5rem);
      background: darken($base-blue, 20%);
      ...
    }
Is 'cleaner' than:

    // HTML
    <div class="baseWidget somethingElse small-rounded-corners dark-blue-bg">...</div>

    // SCSS
    .baseWidget { ... }
    .somethingElse { ... }
    .small-rounded-corners { ... }
    .dark-blue-bg { ... }
It achieves the same reusability (in code) without polluting the content (HTML) with the presentation (CSS).
Are we saying "cleaner" as in "more efficient"? You have to keep in mind the resulting CSS generated after compiling. There is a balance to be had between clean CSS and efficient CSS.

If you are using the classes from the second example in more than one place in your HTML and then depending on your actual CSS in those classes, then the second option may be more efficient then your first. In SASS, it will totally depend on how you write the actual CSS, not just the class versus id debate.

But a question from the article comes to mind in your example, what if you are requested to insert a second login widget to the page? Even worse, if your Javascript uses the ID for targeting the login code then you have to reconsider that code as well.

Maybe I've just seen some particularly bad JS, but I often see a lookup by class name and then just fetching the first element. This pattern would suffer the same fate, without the semantic hint that there should only be a single element that an ID provides. Likewise, in my experience, a lot of code gets more convoluted when trying to be more general than it needs to be (the YAGNI principle) or by trying to be DRY by happenstance rather than by semantics.
If the SASS compiler wasn't smart enough to deduplicate several references to a mixin it would unquestionably mean redundant properties and more weight in the compiled CSS, but it wouldn't be any less efficient from a page paint POV.

There's a completely valid argument that any "improvements" in the above sense are outweighed by the extra time in downloading and parsing a larger cold-cached CSS file, but in my case I am happy to rely on the browser cache and the compiler to be smart (and maybe sacrifice a small amount of resulting CSS weight if it's not) resulting in "cleaner", more semantic HTML (and arguably CSS).

In terms of the second login widget, you're absolutely right, my implementation would fall down, but then it would be a VERY simple refactor. However having n of something on a page would have a semantically different meaning than my case, so therefore classes are indeed the right specificity method!

A case where the "only 1 instance" assumption can break is on a styleguide page, where you might want to include multiple instances of a singleton element to show it in different states, say for your #header for example.

Multiple elements w/ the same id will pick up css styles, though.

(comment deleted)
Or simply don't use CSS at all:

https://speakerdeck.com/vjeux/react-css-in-js

That presentation goes into more depth describing problems with CSS. Given a virtual DOM in JS, the solution becomes simple and obvious: plain objects.

Its interesting how React completely reinvents "best practices".

In my experience, "Don't use X at all" is very rarely, if ever, a suitable response to "X has some flaws".
I agree, and I understand that such "radical" claims invite a dismissive reaction (downvotes). But the presentation really does make a very compelling argument and offers a simple and elegant alternative solution that solves most problems.
Totally agree. I have found that most of the problems solved by separating CSS, HTML, and JavaScript weren't really problems to begin with, if you model your application views the way React does (as a render function that produces DOM tree based on external properties and internal component state). Somehow using multiple technologies (CSS for styling, HTML for markup, JavaScript for logic) got conflated with "separation of concerns", but true separation of concerns involves separating component A from component B. Encapsulating the entire component in JS actually helps cohesion.
(comment deleted)