CSS precedence logic

336 views Asked by At

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 :)

6

There are 6 answers

4
Christoph On BEST ANSWER

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.

1
Oleg On

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 :)

5
Billy Moat On

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

header a#login {
 color: #00F; /* Blue */
}​
11
devstruck On

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.

2
Andrew G. On

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.

0
Ammi J Embry On

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.