I have 4 elements with a class of either x-control-ui
or y-control-ui
:
<div class="x-control-ui">x-control: <output id="x-control-output"></output></div>
<input type="range" min="-4" max="4" step="0.1" value="0" class="param x-control-ui" id="x-control">
<div class="y-control-ui">y-control: <output id="y-control-output"></output></div>
<input type="range" min="-4" max="4" step="0.1" value="0" class="param y-control-ui" id="y-control">
Using the [attribute*="value"]
selector works as expected, selecting them all:
document.querySelectorAll("[class*='-control-ui']")
NodeList(4) [div.x-control-ui.hidden, input#x-control.param.x-control-ui.hidden, div.y-control-ui.hidden, input#y-control.param.y-control-ui.hidden]
but if I change *=
to $=
, using the [attribute$="value"]
selector, then no elements are selected:
document.querySelectorAll("[class$='-control-ui']")
NodeList []
even though though both classnames end in control-ui
.
The same behaviour is observed in up-to-date Chrome and Firefox. I have checked the docs in MDN and consulted other sources, and all seem to indicate that the $=
operator should select elements whose specified attribute (in this case, class
) ends with the given text.
I have tried modifying the selector in various ways, to no avail.
What am I missing?
2
Answers
The thing about the
class
attribute in particular is that it’s kind of an old hack. The classes for an element appear separated by spaces in the class attribute value; it’s just a string. (Now, in 2024, there’s theclassList
API, but 20 years ago that did not exist.)The meaning of the class attribute is such that the ordering of the classes does not matter (well, it might in some bizarre edge cases, but that’s another topic). So a class string of "something foo-control-ui" is functionally the same as "foo-control-ui something". Because code often modifies the class of an element by adding and removing class names, the value of that attribute string at any time could mean the same thing but look different.
Thus, your
*=
selector works because it looks for that fragment everywhere in the string. However, the$=
selector only looks at the end of the class value. Sometimes, it might work, but it’s a very fragile way to write code.As you said in a comment, adding another class to be the "general" quality, and then a more specific class for the precise behavior (or whatever the class is supposed to do), is the more robust way to go. Within realistic bounds, it’s basically no cost to add as many classes as you like: some for CSS rules, some for behaviors, etc.
You are facing problem with the selector
[class$='-control-ui']
, it’s just because this selector seek for those elements whose class attribute ends with'-control-ui'
. but in your html elements the class names ends with other characters likediv.x-control-ui.hidden
. So because of this$= selector
not able to find these elements. modify your html.