I saw Nicole Dominguez tweet this the other day:
say it louder for the people in the backhttps://t.co/prDKo5QaZi
— nicole (@sodevious) January 11, 2018
I wasn’t at this conference, so I have very little context. Normally, I’d consider it a sin to weigh in on a subject brought up by looking at two out-of-context slides, but I’m only weighing in out of interest and to continue the conversation.
The idea seems to be that if you need to select an element in the DOM with JavaScript, don’t use the same selector as you would in CSS.
So if you have…
<article class="article">
</article>
…and you need to apply an event listener to that article for some reason, then don’t use…
$(".article")
(or querySelector
or whatever, I assume.)
Instead, apply an attribute intended just for the JavaScript to target, like…
<article class="article" data-hoverable>
</article>
…and target that like…
$("[data-hoverable]")
The idea is that you can separate jobs. The class has the job of styling, and the data attribute has the job of JavaScripting. Both can change without affecting each other.
Seems reasonable to me.
Also seems like there is plenty to talk about here. Performance, I suppose, but that’s probably the least-interesting thing since selectors are generally pretty damn fast these days. We could continue the conversation by talking about:
- What naming convention?
- Should you be naming events?
- What if it needs to be selected for different reasons multiple times?
- Can you or should you use IDs?
- Is it worth avoiding DOM selection at all if you can?
- What other nuances are part of this discussion?
I saw Michael Scharnagl had some thoughts on his own usage of ID’s, classes, and data-attributes that could help frame things a bit.
I’ve been using a
.js-
prefix for a few years now with jQuery and Vanilla stuff. That separation of concern is great for modularity, so you could have:And then have:
If I changed
component-name
ora-different-component-name
, it would only affect my CSS. That’s a win in my books.I also use BEM style element selectors. So
module-name
could have a child element calledjs-module-name__button
.If that button were to suddenly become an
<a>
tag (plz don’t do that), then it’d be all good as far as the JavaScript was concerned.I’ve been using prefixed classes for this (separation of concerns) for years. I used
.fx-*
for a while but have moved to.js-*
nowadays.I agree with this, it’s what I always do – the data-whatever attributes are extremely useful for this purpose as well as holding bits of data. However css selectors aren’t just for class attributes so the title is a bit misleading. You can use any attribute with CSS selectors, and with CSS I do often use attributes that are not a class attribute. I more often use class attributes, but often I’ll use other attributes (like the data-whatever attributes) when the presence of a style rule only is to apply with elements that have that attribute.
Great topic, and one often debated in teams I’ve been in. Ie the ‘hoverable’ action often depends on the context, or component, that is being hovered. This stops the js selectors being as generic as the demo above.
We’ve thought about using ids or data attributes, but these should be name spaced (especially when building reusable components) as we don’t want any clashes.
We use conventions like BEM (.accordion__item) for classes, which means our data and id attributes end up looking the same. it feels wrong to use classes for functionality but, because of BEM and the way we build components now, I’ve had a hard time argueing against it.
Maybe times and the way we separate concerns have evolved so much that if the shoe fits…
The very old trick of using classes with js- prefix should be enough
We usually use
.js-stuff
here. And use it ONLY for javascripting… Doesn’t look like a problem for me….Personally not a fan of this concept. Call me old-fashioned, but the markup should be semantic and not subservient to the styling (CSS) or behaviour (JS). I’m aware that I seem to be in the minority nowadays, 2-way binding tormenting me everywhere.
I’ve found data attributes work better for storing data rather than selectors. They become redundant if you have a good design system/naming conventions, or you rely on id’s for selecting containers.
Thanks Chris. Something I’ve wondered about recently. I’m most interested in this: Can you or should you use IDs? I noticed the other article talked about their use as a fragment identifier, ARIA, and form elements. Would it generally be better to use attributes for Javascript over IDs?
The guiding rule is that
id
is always unique. If your JS is based on the assumption that there is and will only ever be one element in the page that matches your query, selecting byid
is fine.If you think it’s possible that you might in the future have multiple elements with that functionality, then you should be using a class or attribute to select them.
But, switching from one element to many elements usually requires a lot of re-writing of your JavaScript. (Well, vanilla JavaScript, anyway. JQuery often hides the difference between one element and a list.) So it’s a bigger question than just the selector.
I think it depends on the use case. When you are implementing component where both JS and CSS are needed for the element to behave as desired, I would say using the same selector is perfectly fine, if not better than separating for the sake of separating.
I’d like to see Nacho provide at least one real-life test case where this has been a problem. Especially, this statement:
“The main problem is that you use classes to style elements and your HTML element may change classes (design desitions). If you have JS code bound to that, you will break your code. So use data-attributes and keep classes just for styling”.
Apparently, my entire programming life is doomed! I bind to CSS classes. GASP. My code “will” break!
All he’s really doing is spreading FUD. I’ve only ever bound events JavaScript to classes/ID’s/tags and I’ve never, ever ‘broken’ my code as a result. Good system (and CSS) design dictates that.
That said, reading between the lines of FUD, he introduces a concept I had never before considered and could be useful when implementing data-binding in the future. Of course, there are performance considerations, testing, browser-support, etc a good developer would take into account before jumping the gun and following the dictatorial Gospel of Nacho.
I’ve done this a lot the last couple projects. It’s pretty fantastic. I don’t always use data attributes (sometimes ID’s or js-prefixed class names, depends on context) but the separation of concerns is quite fantastic. I also feel more comfortable making the JS-hooks more functionally-descriptive where I want my CSS to describe content (or failing that some over-all aesthetic).
+1
Would probably use
<div class="js-hoverable">
or something, since I’m guessing you want to change the styling of the element when the mouse is hovering over it.“`
.js-hoverable:hover {
//Styling changes
}
I just prefix classes with ‘js’ (i.e. class=”article js-hoverable”) so there’s a clear distinction between classes used as JS selectors and ones used for styling.
I don’t get it why its wrong to use a css class to select an element with javascript. That is what its for.
And a data attribute is not made up to be used as an selector its made to store data.
I do always prefix my css classes with js- to signal to other developers to not attach any styling to this class. when further classes are needed by javascript i store them in data- attributes.
I agree that mixing class usage both for styling and selectors can be a bad practice.
But I remember a benchmark from a while ago where selectors directly by tag name, id or class were drastically faster than anything else. I don’t know if that still holds true, but I wouldn’t dismiss that as irrelevant.
what about specific class for the event listener? Example: class=”article js-hoverable” where article woukd be specific for css and js-hoverable for the javascript event listener
Absolutely agree with this and use it in all of my projects.
I use the way Harry Roberts wrote about in his article on UI namespacing
Instead of ,
I would write,
Harry also mentions namespaces for QA testing
.qa-
I always use classes with the “js-” prefix so I can easily spot and select elements that interact with JavaScript.
I never thought about using data-attributes, this might be a good idea.
Thank you
This is not entirely true — class attribute is just a selector, and there is nothing wrong to use it to retrieve the necessary bits and pieces. So, if you need to select all article elements, you can use $(‘article’), if you need all elements which have a class article — use $(‘.article’) and so on.
In addition, the “data-*” attributes is intended primarily for storing the invisible data, which can be also used in the javascript.
Therefore, their use as alternative selectors, in my opinion, is not entirely correct. If you want to separate styling and selecting, just use additional selectors, but only if this it is really necessary.
This looks like a flawed cosmetic decision, you say “stop using CSS selectors” but then you go on and use a CSS attribute selector at
$("[data-hoverable]")
.I worked somewhere that had this as part of it’s coding standards, and it worked there.
I worked somewhere that insisted such things are needless additional noise in the HTML, and that adding more JS bindings in order to keep the HTML cleaner is the right call.
Personally, I don’t see that it’s important enough a point for people to take a hard line on. It very much depends upon the system being built, and how it’s bindings are being laid down. A system that tries to minimise component specific JS with generalised bindings being laid down by some library code would benefit from attribute selectors, for sure. A smaller company website type of deal would likely not realise any value from the efforts put into separating those concerns. Horses for courses I guess.
I always try to only target classes with a leading
js-
prefix. My naming convention usually goes along the lines ofjs-(moduleName)-(identifier)
so my code has classes likeThe leading
js-
prefix never gets styled (at least, not by me) so it can be applied to any element. I agree that one shouldn’t target a styled class in JavaScript if at all possibleI´m using “cs-XXX” for styling and “js-XXX” for javascript targeted classes for my projects.
I’ve been using an alternative to this in the form of special class names
…
What about when you work in a CMS ( Drupal ) and you can’t easly edit the HTML, so ( many times ) you can’t add ” data-hoverable “. What would you target instead?
This sounds like a terrible idea. Selectors are easy to use and all web developers and ui designers know how to use them. Not everything that you need to select in javascript can be done by finding all elements with an attribute.
Not to mention pseudo classes.
A better approach if you want to separate out page organization vs functionality/behavior would be to have separate sets of classes (and maybe naming conventions to go along with that).
Remember, classes are space separated lists of class names.
You could have something like this:
Then you can select content based on page organization or via functionality/behavior.
Oh, BTW, the selector that is used to pull out the element with the attribute, that’s still a css selector. It’s just using the attribute instead of the class.
Is there any real difference (other than personal preference) between a data attribute and a JS-named class?
I could be convinced otherwise, but data attributes seem like they would be harder to teach to large teams of mostly “I only do CSS because they make me” developers. Is there any measurable benefit to making that training effort (or even just breaking the habit)?
Well I put stuff in the data attributes that the JS hooks off of, e.g. data-function=”playlistheader” that is useful to my scripting. But it’s mostly style, I could do most of it with js-whatever class names. But only most because sometimes the value of the attribute is more than just an identifier for the script, e.g. data-langkey=”Shuffle Playlist Order” gives a key for updating the title attribute when the user changes the language.
So since I have to use data-whatever attributes anyway in conjunction with the client side scripts, might as well just always use data-whatever for the scripting specific selectors.
I solve this by adding ‘js-‘ in front of JS selector classes.
Just going on what you say in your article I must say that I disagree. It makes more sense to me to use the id attribute to identify the element (isn’t that what it’s for?), and keep the class attribute for styling – there’s your separation.
Why use a different kind of attribute that is meant for storing data, data-hoverable, as an element identifier? It almost seems to be a bit of an abuse of the data attribute and could be confusing to others reading your code.
IDs must be unique. What if you want to apply some fancy hover logic on every post on a page? Are you going to user #post_1, $post_2 and $(‘#post_1, #post_2).do_stuff();?
Nope, don’t agree with it. Classes are HTML classes, not CSS classes.
I second the
js-
(actually I usejs--
to use-
as a space) class naming rather that having loads of data attributes.We tried something similar. At first it was using a JS prefix, then it was custom attributes, then it was data attributes, then it was all of them. At the end of the day what ended up happening was a bunch of redundancy and overlap. For instance, while we use JS to attach events for hoverable, clickable, or keyboard events the JS typically ends up adding/removing classes. Then the logic flows that if we’re manipulating those classes from JS should they be “js” prefixed or should they be data-attributes. Then we end up with objects that look like this
<article data-hoverable class="hoverable js-hover">
because we want to let JS know that an object is hoverable to attach events to, but we also want to let CSS provide specific styling to let the user know it’s hoverable and then when they hover we want the JS to add a hover class so that the styles change for the user to see it’s being hovered over. You start to go down this rabbit hole and wonder if it’s all worth and if we’re not just chasing a model of theoretical purity.Does it matter if CSS looks for .hoverable, .js-hoverable, [hoverable=’true’], [data-hoverable]? Is the extra code worth it? Does it increase or decrease maintenance in the long term and if so how?
There’s no dataattr-toggle while there is classlist.toggle, which makes classes much more pleasant to work with.
I agree on .js-* prefix.
According to this (its jquery, but I think natvie querySelector is the same) there is HUGE performance difference in favor of classes
https://stackoverflow.com/a/19733931
I read about how to NOT use data attributes to find HTML elements with JS. So I wonder what is the most correct.
https://intuio.at/blog/dont-use-data-attributes-to-find-html-elements-with-js/
I’ve been following Harry Roberts/csswizardy’s BEMIT naming convention for a while and find it to solve all the problems with seperating css and javascript.
.js- prefix for any javascript, where there will be no css attached ever, for active states I use .is- og .has- prefixes (say .is-active, is-open, .has-loaded). JS will be aloowed to check for .is-/.has- and add or remove those classes, but never as a primary js dom selector.
Speaking of class=”hoverable js-hover” as some mentioned over. If you follow BEM(IT) your code should be so componentized so much you would never end up with that generic of classes. And in all honesty you should never build your css to be that generic.
Reaction article from Daniel Tan: