Why is the # selector of lesser specificity than anything?
Asked Answered
W

4

10

Big bold caps-lock TL;DR:

I KNOW HOW SELECTOR SPECIFICITY IS DETERMINED, I THINK IT USES FLAWED ASSUMPTIONS AND I CAN BACK MY IRRITATIONS UP WITH VALID SET THEORY RELATIONS, PLEASE DO NOT RESPOND EXPLAINING W3 CALCULATION RULES FOR SPECIFICITY, PLEASE READ THE QUESTION <- read that.

This has bothered me for some time, when I write a style for some HTML that would be similar to below:

...
<div id="outer">
    <span id="inner"></span>
    <span></span>
    ...
</div>
...

Why would specificity rules make the selector "#outer span" more specific than "#inner"? ID's are unique, so when I say "#inner" I can ONLY be referring to one element, so why is it less specific? I understand the rules on determining specificity, I just wonder if this was intentional or accidental, also if anyone knows how I can ask this question to the people who write the css standards.

I should note, I do understand that I COULD use #outer #inner to ensure maximum specificity, but that seems like it defeats the purpose of ID in the first place. This also is a problematic solution for when I write templates and I'm not sure that one ID will be inside of another. I'm not looking for a workaround, just a theory answer.

My question is theory, entirely based on set logic. The though I have is that if you define a rule for 1 item of n possible items, isn't that as specific as you can go? Why would the creators of CSS selectors make a rule that could define m items of n possible items, where m is a subset of n as a more specific rule?

My thought is that #id would be the equivalent of identifying 1 item by name, and #id elm would be identifying a group by its relation to an item by name. It's completely counter intuitive to call a named item less specific than an unnamed group with a named relation.

Walters answered 22/7, 2010 at 17:30 Comment(2)
How do you come to this assumption?Dumpy
@Felix It goes totally against what I would have expected, but it's true. jsfiddle.net/DUKkTImpregnate
T
9

I think the idea of "why" is more a "generational" or "authority" view point. If #Parent (of any generation back) says all my children who meet qualification "x" (in your case, span) are going to be given an inheritance of "y" (whatever css property), it doesn't matter what the single individual #Child wants, it needs the authority of the #Parent to get it if the parent has stated otherwise.

Added on edit: The inline style would then be the rebellious child, and the !important the crack down parent. Edit: I kept this for humor, but I don't think it reflects the idea as well as my later statement below.

Added on edit to question in comment: Given:

#outer span ...
#inner (which is a span element)

Then to help insure #inner selection I recommend:

body span#inner (*edit:* just span#inner works *edit:* if defined later)

or give body an id and

#bodyId #inner

Of course, these can still be overridden. The more "generations" involved, the more it becomes difficult to change the behavior because of the generational consensus (if great grandpa and grandpa and parent are all in agreement, it's likely the child is not going to get away with doing his own thing).

I had to majorly rewrite this section on later edit Given this HTML:

<div id="grandparent">
  <div id="parent">
    <div id="child"></div>
  </div>
</div>

I had previously stated that "#parent div has greater authority than #grandparent div. Both have generational authority, in fact, an 'equal' generational authority, but the first is 'nearer' generation" wins. The error in that is that "nearer" generationally is not what matters, but rather last to be granted authority. Given equal authority powers, the own designated last is the one that wins.

I believe I can still stand by this statement: With that thought in mind, a selector like #child[id] (which outweighs both previous selectors) treats its attributes as permissions for greater authority to rule that which itself controls. Having the # already gave it authority, but not enough to override a # of a earlier generation if that earlier generation also carries another selector granting more authority.

So #grandparent div outweighs #child but not div#child if it is last to receive authority [added this], and not #child[id] because the [id] adds greater authority for the #child to rule itself. If equal selectivity then last one to be granted authority wins.

Again, the style attribute setting a style property itself really acts more like a supreme granting of authority to rule oneself, assuming something more "!important" doesn't take it away.

As a summary statement to answer "why" it is this way (and not in line with "set" theory), I believe it is not about accuracy or really even specificity (though that is the term used) as indeed then one would expect #ChildsName to be the final unique say in the matter because nothing more specific need be said. Rather, however, while the documentation may not state it as such, "selectivity" is really structured on a granting of authority. Who has the most "rights" to rule the element, and given a "tie", who was the last one to be granted those rights.

Ternopol answered 22/7, 2010 at 18:7 Comment(40)
I do like this explanation a bit, but it seems as though there are plenty of ways to say "all children of x parent act in y way" while it's difficult to just say "this specific item, act this way" and be certain that it will, even though i could identify it by name. I won't write a rule for #child if I don't want it to act that way.Walters
I'm out of post votes for the day, but I think you kind of nailed it. Also, your addendum is amusing yet true.Mithras
I see it more like using the #Parent is giving it the generational authority, whereas the #Child alone is acting without parental authority (self authority) and is fine to do so as long as the parent has not said otherwise. So in other words, you, the author are not the parent, the #Parent container is. If you, the "outsider" want to command the #Child what to do, you have to do it through the parental authority if the parent has laid down other rules that might contradict.Ternopol
I do like this the best of the current answers (still to low in rep to upboat), but I don't like it from a set theory standpoint. Since specificity directly deals with sets and subsets, the most specific (sub)set possible would be a (sub)set with 1 item. Since css rules state that there can only be one element of an id on a given page, an Id rule would be a set of 1.Walters
Follow up question then Scott: I write templates and I'm not sure if a parent is going to be uppity and tell it's kids how to behave. I certainly have SOME idea, but not nearly as good of an idea as I would with one file, since my templates MAY or may not use a given element, a given id or a given style. How would I best ensure that something of a specific ID always behaves a certain way?Walters
That's my point, I don't believe it is based off a set theory. Yes, the id is unique, but even so, that id has an authority structure over it. I am a unique individual, and I can tell myself to drive 100mph, but there is an authority structure over me that has stated drive 'x' miles per hour or you may not be driving at all. Or, to keep the generational view, as a 3 year old, I may say I want to eat the whole jar of cookies, but if the parent has said and only provided access to 1 cookie, then that is all I get.Ternopol
I think your example over speed limit laws is flawed. The rules are all of one of 2 authority levels, normal and !important, since I, the author make all the rules, selectors aren't about who makes the rules, its about who they apply to, the idea behind specificity is to determine which selectors more accurately identify a group of elements. What you're telling me is that it makes more sense for #stack_overflow users to dictate your rule rather than #scott. Scott is addressed to you personally, #stack_overflow isn't. The tags have no desire, on my website I am god and I tell them how to behaveWalters
I don't think specificity in css is about "which selectors more accurately identify a group of elements" as it is which selectors more authoritatively identify a group of elements. If you or another css author has said this property is controlled by the #Parent, you nor another author has the right to tell #Child directly otherwise without either 1) going through the chain of command of parental authority, or 2) changing the original parental authority in that area.Ternopol
I don't mean to be abrasive, Scott, but I don't see anything in the w3 specification for selectors that describes a selector about authority or a chain of command in any sense. My "opinion" of specificity and selectors was a paraphrase: "In CSS, pattern matching rules determine which style rules apply to elements in the document tree. These patterns, called selectors, may range from simple element names to rich contextual patterns. If all conditions in the pattern are true for a certain element, the selector matches the element." w3.org/TR/CSS2/selector.html#pattern-matchingWalters
Reading through the w3 document on specificity was the reason I thought this whole situation to be an unintentional side effect.Walters
No abrasiveness detected. Notice, however, in your quote that 1) it says nothing about accuracy or authority (the difference between our thoughts on this), but 2) it implies the authority view by the fact that it is a document tree (much like a family tree) and the last phrase "if all conditions in the pattern are true" a match occurs also fits with the authority view. The more generational matches, the more authority. There is nothing more or less accurate about #Child verses #Parent #Child, both refer to the same unique individual, but there is a world of difference in authority.Ternopol
I think the problem I have with this is that it requires extra specificity for ordinary situations, while an alternative solution based on set theory would not. The rules also seem to defy you "document tree" viewpoint, because of the following situations: jsfiddle.net/kKTJn/1 I can't take the "document tree" viewpoint seriously with the last 2 being more specific. Clearly this was a design flaw based on set theory.Walters
If you agree with #inner being red, I don't see how you can agree with #innner[id] being green. Essentially in a child parent relationshop, that'd be like me saying you shouldn't respond to "#scott" if your parent tells you that your element shouldnt, but you should respond to "#scott" if you have a name (always true) regardless of whether your parent says your element is allowed to or not.Walters
jsfiddle.net/kKTJn/4 and this one to add more elements that #inner[id] won't care about either.Walters
Actually, looking at your link helped me clarify my thoughts. It is not so much that setting style is like being a rebellious child, but rather that attributes act as another generation of authority. I'm going to edit my post above to clarify.Ternopol
Alright, I look forward to it, but I'm still not sure that saying element by the name "x", that also has a name, is more specific than element by the name "x". Having a specific name necessitates having a name, so only by this standard is it more specific, and in my mind mistaken.Walters
You are hung up on "specific" verses "authoritative." Both are equally specific, and in a sense "redundant", but it is a granting of authority not of specificity as both equally target the element. To go a bit medieval, I am "Scott" but if one with authority knights me "Sir Scott" then now I am #Scott[sir] :-).Ternopol
Scott, in the rewrite edit that you added, you stated that div#child{} outweighs #grandparent div{}, unfortunately it doesn't outweigh #grandparent div div{} or #parent div{}. My problem with your authority viewpoint is that it doesn't prevent tyrannical rule from parent elements. The idea that you would have to be MORE SPECIFIC than as specific as you can be in order to override parent rule is absolutely absurd, and is the reason why a majority of rules default to "inherit". The purpose of styles is to declare children that have differing rules from parental rules.Walters
My point was initially that if I wrote a rule that fit the pattern (excuse regex mixed with psuedo) ^[selectors]*#id_selector$, in any given document there are 1 or 0 possible elements that could match, because it ended with an ID, which have to be unique per document. Barring cases of shared stylesheets and misuse of id consistency between them (in which case, more care should be taken writing the page), why would I even write a rule that I wanted to be overwritten? Every rule that I write in #inner is a waste because it will never be used by any other element if #outer div is overwriting it.Walters
div#child will outweigh #parent div if the former is defined after the latter. If you have granted "tyrannical rule" to the parent elements by giving it greater authority (each selector added grants greater authority, assuming it resolves to actually matching the element in question), then indeed the child should not be able to override the authority you gave the parent unless you give the child greater authority to rule itself than you granted the parent.Ternopol
Just because #child is unique does not mean it should necessarily grant greater authority than that which you granted #parent to rule its children by using #parent div. The #parent div gives it authority over every div in it of a weight equal to # + the tag name, whereas #child only gives it authority of weight # and needs to have weight of tag name added (div#child) to have equal authority to the parent, and then, with equal authority, it depends on which you (the author) granted last.Ternopol
If you always defined your #parent div early in the css, and direct div#child later, no parental override will occur assuming you don't grant greater authority to the parent (#parent div div).Ternopol
Bleh, I had to give you the solution. It does make sense now. I started making a jsfiddle and when I realized that what I was expecting wasn't in accordance with inheritance, namespace and class rules that I use with standard OOP programming. In my Jsfiddle, I wrote a class that I was saying I wanted to span the whole page, but when I realized how frequently I reuse variable names in differing scopes, I realized that in any ordinary programming language, I'd have to declare that a given variable acts within another scope as well. Here's the jsfiddle I self-realized jsfiddle.net/9u8fF/1Walters
Nevermind on the #parent div / div#child, I misspelled parent in my jsfiddle.Walters
I am glad "it does make sense now" to you. I felt you were hung up on the fact that the id is unique and therefore should have absolute authority over itself when (even in the "real world") uniqueness and the authority to do what one wants have no necessary relationship to one another.Ternopol
Likewise, if I had a variable x and I made a rule for it, it becomes an instantiated object which inherits everything but the rules i set for it. calling a variable y by name makes no difference if it is now a member of x, because it's name isn't y anymore, it's x.y; I'm unable to call inner because it's not in the global namespace, it's within #outer's namespace, and it's name isn't #inner, it's name is #outer #inner.Walters
I think the problem was that I didn't infer what you meant by authority, since the elements are elements, and are told what to do by rules that I write. I think a better explanation comes from inheritance, instantiation and scope.Walters
My idea was that I was calling the object by name therefore I was specific, the problem wasn't that I wasn't specific, it was that I wasn't calling it by its name. The outer scope had absorbed it's name and added the outer name through its instantiation in the global scope.Walters
I follow you (I think), but am not myself foremost a programmer (more a designer, my skills reside more in css manipulation), yet I do have some programming knowledge. If "inheritance, instantiation and scope" explain it better, then what of the fact that #outer div being overridden by #inner[id]? In your "scope" view, #inner is still in the scope of #outer div, what does the [id] do in your programming viewpoint?Ternopol
That's where it all gets very complex. The idea behind the point system is that there are multiple concurrent scopes. When the CSS parser is reading the rules, it has a ranking system for what scope the item goes in, and it determines which rule ranks highest before instantiating the objects under a certain rule. Each selector type: <element style="">, #id, .class, :pseudo-class, [attribute], :pseudo-element and element and variants of such have their own scopes, which are ranked.Walters
When an element is instantiated into an object, it can be referenced through that scope to write a rule, or inherited into another higher ranking scope. In the case of #outer span vs #inner, the #outer declares the first rule to be a scope as the intersection of the #outer scope and span scope, ranking it acordingly. When the rule #inner comes along, the object named with the id inner is being reserved for instantiation within a scope (#outer span).Walters
In order to change this item, it can be called by name through the scope it currently resides, it can polymorph parts of itself that are already declared into another higher ranking scope or it can instantiate parts that are not yet declared into a lower ranking scope (essentially polymorphing global inherited values to something else). #outer #inner would be addressing the object through it's current scope, and #inner[id] would be a rule for the intersection of #inner scope and the [id] scope.Walters
Since the parser recognizes #inner[id] as a higher ranked scope than #outer span, it instantiates part of it's rule within an alternate scope. The scopes are all virtual, so this chopping and slicing of instantiation is closer to something like being born on the border between two countries, and each has specific rule on you. I think the whole discrepancy here now is a tomato/tomato (hey you said those the same way in your head) type of dispute. We're both talking about the same thing now, just in different metaphors.Walters
If it were to go looking for #inner, it'd find that #inner was already marked to be instantiated by the #outer div namespace rules. If the #inner was a reference within that namespace or a scope which outranked that namespace, it could make adjustments, but in this case #outer div is ranked higher and it won't make changes to rules that are already declared in that namespace.Walters
I agree, your determining of scope sounds like my granting of authority. You even mention "higher ranked scope" which sounds like a synonym for "higher authority".Ternopol
Indeed, my rank is your authority from what I can tell. The problem I had understanding your explanation is that you didn't give a definite structure to how authority was granted. Overall, I like this, and in my head I have this disturbingly unusual mental image of code being instantiated in different scopes.Walters
I was visualizing the parent making the child take their name too, which is too simple of an assumption, since there are a ton of different types of parents (each selector type combination), and each type of parent has more authority than another.Walters
Well, one reason I didn't give a structure of how authority was granted was because that is the specificity rules which you so very certainly made clear you understood. I was simply trying to get you to see that it was authority based. Being mainly a self trained "programmer" (not sure I can claim that title, only knowing some javascript and php), I struggled myself to entirely follow your metaphor, and certainly would not have thought of putting it in those terms as you have. Between our metaphors in this thread, I think just about anyone should be able to grasp the "why" of css specificity.Ternopol
Certainly. I come from a python/C background, and the idea of scope or namespace having unusual nesting isn't so far-fetched as it might be for javascript. I think if I had to sum it up in one sentence, I think it'd be: "W3 redefines specificity using their system in order to handle the heirarchical needs of CSS."Walters
I'm so glad this extended conversation was recorded in comments instead of being moved to chat. Awesome.Josephjosepha
M
3

Because #outer span has both an ID selector and an element selector. That element selector is what makes it weigh more than #inner.

The former means 'select any element found within any element of ID outer'.
The latter means 'select any element with ID of inner'. It doesn't know where #inner is in your HTML document, hence less specificity.

Perhaps you could either try #outer #inner or span#inner try #outer span#inner instead.

Mithras answered 22/7, 2010 at 17:33 Comment(5)
Apply the same principle to a different situation, usernames. If I say "BoltClock" I've identified a user that I'm talking about by name, you can no longer become any more specific to which user. If I were to say "BoltClock's commenters" I'm talking about a more specific subset of users than ALL USERS, but not necessarily just one, making it less specific in terms of set notation. The whole system uses flawed set logic.Walters
Thanks for the workarounds, but being the really stubborn person that I am, I have to insist that their system for identifying specificity is wrong, and refuse to change. I usually just do #inner{rule:rule !important;}Walters
I've always wondered about this myself too, actually, and found it equally counter-intuitive at first. But I guess everyone's different - I've been adapting peacefully to the way CSS does it.Mithras
Questioning authority is never a bad thing, somebody has to think about web standards right? If it doesn't make sense, there should be a discussion. I figured maybe it did, so I asked.Walters
BoltClock, i prefer the following now: #inner[id]Walters
C
1

How

W3C rules for calculating specificity:

  • count 1 if the declaration is from is a 'style' attribute rather than a rule with a selector, 0 otherwise (= a) (In HTML, values of an element's "style" attribute are style sheet rules. These rules have no selectors, so a=1, b=0, c=0, and d=0.)
  • count the number of ID attributes in the selector (= b)
  • count the number of other attributes and pseudo-classes in the selector (= c)
  • count the number of element names and pseudo-elements in the selector (= d)

The specificity is based only on the form of the selector. In particular, a selector of the form "[id=p33]" is counted as an attribute selector (a=0, b=0, c=1, d=0), even if the id attribute is defined as an "ID" in the source document's DTD.

Concatenating the four numbers a-b-c-d (in a number system with a large base) gives the specificity. Also, when rules have the same specificity, the last one wins.

Example

  • outer span: a=0, b=1, c=0, d=1 --> 101

  • span#inner: a=0, b=1, c=0, d=1 --> 101
  • div#outer span#inner: a=0, b=2, c=0, d=2 --> 202

Try rearranging rules 1 and 3: http://jsfiddle.net/Wz96w/

Why

My thought is that #inner does not specify a unique element. While it is only 1 element per page, that ID could be a completely different element on another page.

One page:

<div id="outer">
  <div id="inner"> ... </div>
</div>

Another page:

<ul id="outer">
  <li>main1
    <ul id="inner">
      <li>sub1</li>
      <li>sub2</li>
    </ul>
  </li>
  <li>main2</li>
</ul>

Although, I would not code it this way. I think it explains why adding the element (ul#outer vs. #outer) is worthy of extra specificity.

For the point on descendants, I'm visualizing the DOM for your markup. The rule #outer span has a path length longer than that of #inner. Therefore, in the case, it specifies a more specific subtree, so it should be awarded more specificity (and #outer #inner li should be [is] worth more than #inner li).

Carmencita answered 22/7, 2010 at 18:0 Comment(4)
Copied and pasted, I apologize for ruining your effort, but the question was about why things are the way that they are, not how to obey them: "Again, I can't emphasize this enough: I'm not asking a question of HOW the #_id_ selector is less than #_id_ element selector, I'm asking why it's made that way."Walters
I do appreciate the example, T, but your own sentence should disclaim this example: "Although, I would not code it this way." Shouldn't standards be written for a good coding style? If I attach a style sheet to a page that calls an element by name and tells it how to behave, shouldn't it obey that above any generic rule?Walters
I added clarification to express that #inner is not necessarily used on only one element; it may be a different element on other pages. That is part of my hypothesis for why the W3C chose to calculate specificity this way. Interesting question btw.Carmencita
I definitely think that elm#id and #id1 #id2 should rank higher than #id, fixing situations you proposed. but I don't think that #id1 elm should rank higher than #id2, and I don't see that as necessary for a shared stylesheet.Walters
O
0

To me, and I base this entirely on opinion, it's the expected "natural" behaviour.

Consider this:

You know how CSS specificity is calculated, and from that formula we know that #outer span is more specific than #outer, which is necessary for CSS on the whole to work correctly, and it makes sense. #outer span is also more specific than #inner, which is also logical within the domain of the stylesheet (#inner is only an ID, and #outer span is an ID plus an element, so in order to rank them if we are just looking at the stylesheet, the more qualified one must be more specific).

What's happening here is that you're applying the context of the HTML markup, and saying "Well, that doesn't make sense." To make things work the way that you're expecting, the browser would have to consier the following:

  • This <span id="inner"> is inside <div id="outer">
  • The stylesheet rules for #outer span and #inner apply
  • The rule #outer span is more specific than #inner
  • But wait! <span id="inner"> is inside <div id="outer">, so ignore the calculations based on the stylesheet and claim that #inner is more specific

That last step makes the determination process entirely based on the structure of the HTML, which makes it impossible to define the specificity in terms of the CSS alone. I personally believe that this would make the entire process more convoluted and hard to define, but you may disagree.

Oversew answered 22/7, 2010 at 18:31 Comment(7)
You meant "id" not "class" in the first paragraph. I'm not sure how to send PM's in SO, but I understand. I'm not applying HTML here at all, I understand that CSS has rules in HTML, and no id can be used more than once in a page, meaning I can only be talking about 1 element when I write a rule for an id only. My question is why would I ever want rules that I write for that lone element to be overwritten.Walters
Your whole process could be bypassed with that simple understanding. It's not valid HTML if an id is declared twice, therefore any rules by id can only be talking about 1 element.Walters
Whoops, not sure what I was thinking. Thanks, I corrected that now. And you're right, but how would you define that in terms of what you can determine in the CSS alone? There's no way using the CSS specificity rules that #inner could be more specific than #outer span, because #outer span must be more specific than #outer. We know that #inner could be considered more specific once we get to the HTML, but when we're parsing the CSS I don't see a good way to know/define that.Oversew
We could defer the decision until we look at the HTML, but for reasons of implementation that might not be optimal. Naturally if #inner isn't a descendant of #outer, the rules aren't at all related, so it wouldn't necessarily make sense to say that #inner is more specific than #outer span, since they're not related. I think there's room to say that the formula as a whole is flawed in the sense that it doesn't take these scenarios into consideration, but I do feel like the "why" is routed in what's possible to determine upfront using that formula.Oversew
Right, but you can't say that #inner is more specific than #outer, so when using a numbers-based system while parsing just the CSS I don't see how you can define #inner being more specific than #outer span, since it's #outer + span. I'm under the impression that the numbers-based system was chosen because it was the most consistent method by which to determine specificity, but I could be wrong. Naturally, as mentioned before, it lacks the ability to make these kinds of determinations, but seems fairly straight-forward to implement.Oversew
because #inner is identifying an id, and #outer span is identifying any element of an id. #inner HAS to be 1 item or none, #outer span could potentially be every item except outer.Walters
Yeah, I totally understand what you're getting at, and I agree on a conceptual level. I'm just saying that the numbers-based system they decided on cannot describe that situation, and I think that they chose the numbers-based system because it provides the most consistent results, even though it doesn't work for this kind of scenario.Oversew

© 2022 - 2024 — McMap. All rights reserved.