Hacker News new | ask | show | jobs
by mattigames 3021 days ago
There is not a single time I had though to myself "This should have been an ID instead of a class"; but there are hundreds of times where I have though to myself "This should have been a class instead of an ID".
4 comments

Interesting, I'm the other way around, I've defaulted to classes and then found that Ids would be more appropriate in many cases.

My simple rule for id vs class (as a non-expert) is if you say 'this is the x' e.g. 'this is the footer/header/navigation-panel' then it's an Id, if you would say 'this is a y' e.g. 'this the a product/list-item/image' then it's a class.

In terms of pure CSS it doesn't matter, but for JavaScript that manipulates the DOM (e.g. jQuery), it does. Specifically, `getElementById` is more than 50% faster than `getElementsByClassName`.

https://jsperf.com/id-vs-class-hn/1

That micro benchmark is comparing getElementById which is a map lookup (assuming the id is unique in the document) vs getElementsByClassName which is creating an iterator that would scan the document as you loop over it, except that the page is not looping over it or calling .length, so it's not really scanning anything at all.

Essentially it's benchmarking a handful of branches inside getElementById that do the map lookup for an Element [1] against the handful of branches inside getElementsByClassName that do the map lookup for a NodeList [2]. They both look pretty similar to me in branch and map lookup count, so I'm not sure where the difference is, but it's not something that would impact real content since on a real page getElementsByClassName's cost is related to typical behavior being O(N) instead of O(1) like getElementById. Browsers have a ton of caching around it though, so getElementsByClassName can look like O(1) in micro benchmarks.

You could could alter the benchmark to do getElementsByClassName("my-class")[0] to test the scanning but browsers cache that too, so you'd need to actually modify the document between each run of the benchmark somehow (ex. adding or removing an element) to invalidate the cache.

Typical content that uses getElementById will be faster than content that uses getElementsByClassName, but it's hard to show that in a benchmark. :)

[1] https://cs.chromium.org/chromium/src/third_party/WebKit/Sour...

[2] https://cs.chromium.org/chromium/src/third_party/WebKit/Sour...

Well if what you style is the main content/header/footer area, and there is should only be one of it per page, using an id is a good way to enforce it as well as document it.

If you made it a class, I would assume I can reuse it elsewhere.

Now it's ony true for stuff you can't reuse, but unless you are building a boostrap competitors, there are actually a lot of things you never reuse in a real website.

Once I needed to make an internal IE addon to listen for certain elements being selected on pages outside of our control. (to ensure we never collected sensitive data for compliance reasons, long story. Trust me it makes more sense in context than it does in a single paragraph.)

My co-worker wanted to grab by id and attach listeners, but I was convinced that wouldn't work. I wanted to listen at the top level and filter down by id/class/etc...

The first production test was <redacted>.com/login. It had a login widget on the navbar and a login widget in the main content section. Both the username and the password field had the same id in the navbar and the content. So the addon did the right thing on the navbar, but not in the content, and we had to go back and redo it thanks to id reuse.

My point is, elements with ids are rarely unique. I've never regretted using a class instead of an id. I've regretted using an id instead of a class. And I really regretted third parties using ids instead of classes.

Please don't use ids. Your unique elements won't be for long, I guarantee it.

A friend once told me about a girl in their cs 101 class, who learned about arrays, and proceeded to make every variable an array. When queried, she responded, "I might end up realizing I need a multiple of the variable, and this way I won't have to change anything when I do".
Did she then go on to create jQuery? Because that concept (with a touch of functional inspiration) is one of the underlying ideas behind the library and why it was considered so much easier to work with than the DOM API.

I get where you're coming from, but I don't think it's a good reason to avoid classes. That's what they're there for.

Not at all; some simple effects like a stick-to-the-top header usually require cloning such element using JavaScript; and even if such effects are not needed right now they may be added in the near future, so no, there is not a single time where having an ID over a class makes sense.
Since I'm more of a backend dev, I'm going to take your advice and try it on my next work. Let's learn something today.
I tend to reserve IDs for things that exist only once on the page at a given time, and the only properties I define under IDs are ones for layout. For example, if I have a page for an article, I would make the top-most element "#article" and things like the navbar and the body "#article__nav" and "#article_body". The only thing those IDs are concerned with is how the navbar and the body are sized and positioned when on the article page.

Shared stylistic properties for components use class names. The properties that make a navbar look like a navbar(color, font, and anything that applies to children) would be specified under ".nav" and the same would apply for the body under ".body". Anything that's stylistically-specific to

Shared variations of components are handled with modifiers. An example would be ".nav--dark".

Any one-off variations are handled with parent-scoped class names like ".article__nav", since I treat even that topmost element as a component. (or a block if you're thinking in BEM).

I've found that keeping "style" and "layout" CSS properties separate helps keep components flexible and untangled from a given page layout. The differentiation between IDs and class names makes this more convenient because it becomes obvious what parts of my CSS are intended for.

But what about the internal layout of components? If the children of a component are supposed to change their sizing and positioning at different breakpoints, then how are you supposed to achieve this while keeping components agnostic about the page layout?

It's a tricky problem, but I solve this with Sass mixins. If a component has different internal layouts for mobile and tablet screens, as an example, I write mixins to define those states using the same naming convention that I would use with class names and IDs. If I was using a grid system, I would define them as "@nav--lg", "@nav--md", "@nav--sm", "@nav--xs", etc. Then underneath the page layout, in this case "#article", I would include those mixins under the chosen breakpoints. That way, all layout can be handled by IDs in stylesheets dedicated to page layouts.

It just so happens that I have a Codepen that demonstrates this idea:

https://codepen.io/Ravenstine/pen/xpYoWv

The only part that is different is that I made the masthead and the footer not a part of "#article". If you don't plan on having components like that behave differently depending on the page they are on, that's probably appropriate.