4

In this example:

http://jsfiddle.net/Ja29K/

<style>
  /* Default links */
  a {
    color: #0F0; /* Green */
  }

  /* Header links */
  #header a {
    color: #F00; /* Red */
  }

  /* Login link */
  #login {
    color: #00F; /* Blue */
  }
</style>

<header id="header">
  <p><a href="#">header link</a> is red</p>
  <p><a id="login" href="#">login link</a> is not blue</p>
</header>

Is not logical that the login link must be blue?

I know that the declarations have the same origin and same importance, so they need to be scored (selector's specificity).

To calculate selector specificity I created an table for each selector:

A = Number of inline styles: 0
B = Number of ID: 0
C = Number of classes: 0
D = Number of elements: 0

So the login element have 3 collisions related to his color: a, #header a, #login

element (A, B, C, D)

a (0, 0, 0, 1) = 1
#header a (0, 1, 0, 1) = 101
#login (0, 1, 0, 0) = 100

The selector "#header a" wins because it had the biggest score.

But

If we change the selector "#login" to "a#login", we will have:
a#login (0, 1, 0, 1) = 101
The selector "#header a" looses, because with a draw wins the last that was declared.

So, the thing that I can't understand is:

Since "#header a" selector refers to many elements and an ID selector (e.g. #login) just refer one element, is logical that we want to apply ID selector declarations to that element, right? I really can't understand this CSS precedence logic, because I think ID selector must be basically the most specific thing possible, just like inline styles.

P.S.: Sorry for my bad english :)

Oriol
  • 274,082
  • 63
  • 437
  • 513
André Abreu
  • 737
  • 7
  • 13

6 Answers6

4

No, according to the logic of selectors, it is not.

#header a is more specific than #login. If you reduced your #header a selector to #header, then the header selector and the login selector would have the same specificity, and the rule that was last expressed (in your order the color from login) would be used. The same would be true if you increased the specificty of the login selector by adding a tag name to it.

devstruck
  • 1,497
  • 8
  • 10
  • 1
    Another best practice here is use `#header #login` for the last rule, making it overrule the `#header a` one. – behnam Aug 20 '12 at 21:08
  • 2
    The problem is the assumption that the single id selector is more specific than an id selector a tag. Selectors have to be able to operate regardless of how a developer chooses to organize their DOM. The only thing the browser knows is that both `#login` and `#header` refer to a single, specific DOM element with the noted id attribute. `#header a` refers to a subset of an id-based rule, and is therefore more specific than any applicable (single) id-based rule. – devstruck Aug 20 '12 at 21:15
  • @AndréAbreu Could you perhaps point out where my argument fails to explain that? – devstruck Aug 20 '12 at 21:17
  • 1
    @AndréAbreu because (1, 0, 1) is bigger than (1, 0, 0) by definition. – behnam Aug 20 '12 at 21:18
  • @André #login a is more specific as #login although the latter can only refer to exactly one element. The terminology "specific" implies a stricter subset of elements so that #login should be more specific. – Christoph Aug 20 '12 at 21:18
  • @post_erasmus If I have "header#header a" and "a#login" the browsers knows that "a#login" refers to a link element, but "header#header a" still wins :) – André Abreu Aug 20 '12 at 21:37
  • @behnam I know that, I just cant understand Why this logic. – André Abreu Aug 20 '12 at 21:38
  • @AndréAbreu By your logic, a selector for the `

    ` tag should be considered equally as specific as an id selector, because a body tag can only occur once per page. Other than identifying what elements of the DOM to apply a given rule to, however, selectors don't care about which tag or class or id they are using to derive the specificity score. To do so would make CSS implementation (both for developers of web sites and for developers of web browsers) far more complex. When designing systems that will be used by others, simplicity and universality are really, really good things.

    – devstruck Aug 21 '12 at 13:24
  • @post_erasmus Thanks for your comment. I don't think that

    tag should be equally as specific as an id selector. IDs selectors are meant for singletons, and tags selectors not (expect some tags of course like body). I think that ID selectors must have the "same power" of inline selectors. I think that is more complicated adding more weight (like a#login) to your ID selector just to take the precedence, right? I agree with you, I know that CSS implementation must be simple like everything else, but I think this is just a kind of logic.

    – André Abreu Aug 21 '12 at 16:53
  • @Christoph: `#login` does not limit itself to one element. It merely says "any element with an ID of `login`" without regard for whether it is unique or not. Since `#login a` has a type selector accompaning it, it's not quite correct to say that `#login` is more specific. Combinators are also not relevant to specificity (although they're implied to be sometimes). – BoltClock Sep 28 '12 at 21:04
  • @post_erasmus: Correct. The source document type is completely irrelevant when calculating the specificity of a selector, because we're talking about CSS selectors, not HTML. While it's true that we write selectors and apply rules based on assumptions of an HTML structure, strictly speaking these are little more than assumptions, whereas technically the nature of the source document has no bearing on selector specificity. – BoltClock Sep 28 '12 at 21:04
1

You can't see "specificity" in a sense of which selector targets the fewest elements but simply what is most important.

Of course could the rules have been made even more complicated by differentiating such things like #header a or a#login. However this just would add more confusion to the whole system.
Also most likely this (c/w)ould be abused like the following: header#header a - this added a higher specificity but also could target more elements.

In my opinion this would add no further value to the system but only make it more complicated.

When writing CSS one should always try to keep the rules as short as possible for performance issues. If you need to overwrite a rule you still have the possibility to add another id or class - in addition to the normal cascading this is really more than enough.

Christoph
  • 50,121
  • 21
  • 99
  • 128
  • @chirstoph Is not see "specificity" in a sense of which selector targets the fewest elements, but if exists one selector that target just one element just apply it! Just like inline styles :) – André Abreu Aug 20 '12 at 21:48
  • when I say "one selector that target just one element", I am saying ID selectors. – André Abreu Aug 21 '12 at 01:34
  • @André A tag or class-selector might as well only target one element, depending on the markup. Since always all parts of the selector are evaluated, the key selector has no "extra weight" in how specific a selector is. I agree, sometimes it is annoying, but that's what the spec defines. – Christoph Aug 21 '12 at 07:37
  • The spec doesn't even define the term "key selector". However, it does define the term "subject of a selector", which is similar but not exactly the same. – BoltClock Sep 28 '12 at 21:08
1

You seem to be familiar with the concept of specificity, which is thouroughly described as part of w3 css specs. From the algorythm perspective, selector specificity values in the rule declaration are flat-weighted or non-positional. This means that #header a and a#login have the same specificity, meaning that if an element is eligible for both rules, the latter one will take precedence.

Personally, it took me much longer to come to terms with selectors having semantic specificity but no calculatory value. For instance, ul li and ul>li have the same weight even though the latter "feels" more specific!

I find that anyone with functional programming background finds it easier to compare specificity as four-byte values (this is in fact close to how it's implemented in major browsers - consequently overflowing the value when 256+ selectors of the same weight are used :)

Oleg
  • 24,465
  • 8
  • 61
  • 91
  • 1
    You may find [this question](http://stackoverflow.com/questions/8096829/why-do-foo-bar-and-foo-bar-have-the-same-specificity-in-css) interesting; it looks at why `ul li` and `ul>li` were designed to be equally specific. – BoltClock Sep 28 '12 at 21:06
0

It's just down to specificity - be more specific and it will work for you:

header a#login {
 color: #00F; /* Blue */
}​
Billy Moat
  • 20,792
  • 7
  • 45
  • 37
  • 1
    Or use `!important` :-) Although this helps the situation, it doesn't really answer the question. An id (`#`) is basically the most specific thing possible. – Imp Aug 20 '12 at 21:02
  • 2
    @Imp `!important` is a very bad choice and should not be used in stylesheets. – Christoph Aug 20 '12 at 21:04
  • `!important` doesn't need to be used outside of stylesheets. But in some cases, it's useful. For instance: if you want `.required` to be red anywhere in a page, even if a more specific style colors it black. – Blazemonger Aug 20 '12 at 21:16
  • @Blazemonger well, and then you need one element having the `.require` class to have green font. You increase the specificity of the selector but nothing happens due to the initial `!important`, so you add another `!important` which leads to another important which leads... in the end literally each of your rules have an `!important` and you are back at the point where you just should have written clean CSS in the first place. – Christoph Aug 20 '12 at 21:34
  • @Christoph I cannot possibly imagine a scenario like the one you just described. – Blazemonger Aug 20 '12 at 21:40
0

OP, perhaps you could think of it that CSS processes the first argument (#header, and #login) first, and only after that, then it processes the second argument (a in "#header a").

So on the first process, it's made red, and then blue, but on the second process, it's overwritten to red, due to the "a" in the second argument.

Andrew G.
  • 439
  • 4
  • 10
0

All it takes to fix this is changing #login to a#login letting the DOM know this command is specific to a link.

The #header a is more specific than just #login because it's pointing at a specific element in the DOM, not just a random id.

Roddy of the Frozen Peas
  • 14,380
  • 9
  • 49
  • 99
Ammi J Embry
  • 637
  • 4
  • 17