8

My main goal is to try to reorder a CSS style block based on specificity. I previously had helped from SO and I managed to produce this function. See gist.

Here is an example:

function specificity($selector){
// https://gist.github.com/2774085
}

$compare = function($a, $b) use ($specificity) {
    return $specificity($a) - $specificity($b)
};

$array = css_array();

uksort($array, $compare);

The above has been working great until I came across this CSS:

html, body, body div{
    background: transparent;
}
body {
    background-color: #D96F02;
}

This gets reordered into this:

body { // 1 point
    background-color: #D96F02;
}
html, body, body div{ // 4 points
    background: transparent;
}

However, this isn't how the browser applies the CSS.

I think my specificity function may be missing the importance of order of CSS rather than ordering based on selector specificity? Is this true?

Update

I think what I should do is in my compare function, I should always add an extra points of say 10 because specificity isn't just based on selectors its based on the order of the selector too. So something like this:

 $compare = function($a, $b) use ($specificity) {
        return ($specificity($a) + 10) - $specificity($b)
    };

How does this look and how do I determine number of points to give in this case!?

Community
  • 1
  • 1
Abs
  • 51,038
  • 92
  • 260
  • 394
  • isn't background shorthand by default more selective? have you tried it with background-color:transparent? – albert May 23 '12 at 09:22
  • @albert are you saying that I should be ordering even at the CSS property level? I think the browsers only order by selector and then apply the CSS properties, right? – Abs May 23 '12 at 09:25
  • Hmmm interesting problem :) Firefox extension Firebug has that solved but for one element right (inspect function). Try figuring out how they did it. Starting point, maybe http://code.google.com/p/fbug/source/browse/branches/firebug1.10/content/firebug/css/cssElementPanel.js – Matej Baćo May 23 '12 at 09:37
  • Can I ask why you're trying to do this? What's the use case? – thirtydot May 23 '12 at 09:39
  • @thirtydot I am basically trying to create a better CSS inliner. I've used many others but they all have their faults. I'm trying to create a bullet proof one. The last step that I have now is how to consider selector order, I can calculate specificity but how many points should I give for order? – Abs May 23 '12 at 09:52
  • idk man. i ran your css through this specificity calculator and your scores are correct: http://www.la.unm.edu/~bjudd/IT152-CSS/resources/specificity.php – albert May 23 '12 at 12:09
  • @albert my specificity calculations are correct. However, because I am reordering the CSS based on the specificity. I need to consider the original order they cam in. Do you see what I mean? – Abs May 23 '12 at 12:16
  • 1
    You're *never* going make a bulletproof "CSS inliner" unless you *completely* understand how CSS and browsers handle this. If you just guess (`+10` to specificity?!), it won't be robust. The spec: http://www.w3.org/TR/CSS2/cascade.html. – thirtydot May 23 '12 at 12:52
  • 1
    I disagree that your specificity calculations are correct. What you show as `4 points` should actually be calculating three separate specificity ratings of 1, 1, and 2. The comma's make it three different css selectors in one line, not a combining selector like `html > body > div` or some such. – ScottS May 24 '12 at 03:01
  • @MatejB: It's precisely because it does it on a per-element basis, [just like how browser engines do it when matching rules](http://stackoverflow.com/a/5813672/106224). – BoltClock May 24 '12 at 05:36

1 Answers1

4

The problem should go away if you serialize

html, body, body div{ 
    background: transparent; 
} 

as separate rules, as if they were written in the following manner:

html{ 
    background: transparent; 
} 
body{ 
    background: transparent; 
} 
body div{ 
    background: transparent; 
} 

The semantics is a bit confusing, but basically you are judging an entire declaration based on independent selectors that have been combined into the same rule set.

Re your update: specificity is not affected by selector order, i.e. the rules below have the same specificity. The last rule will take precedense rendering in blue, unless you re-arrange the rules rendering in red:

<style>
    #container span {color:red;}
    div #content {color:blue;}
</style>

<div id="container"><span id="content">Hello world!</span></div>
Oleg
  • 22,838
  • 4
  • 55
  • 82
  • 1
    And neither is specificity affected by how "close" the combinators and selectors are to the actual element that is matched, because there is no such notion of proximity in selectors. – BoltClock May 24 '12 at 05:31
  • @BoltClock: this is somewhat confusing when applicable to child selectors `a>b` being no more specific than descendant selectors `a b`; child selectors just "feel" more specific (after all, by definition a child selector would normally mach a smaller subset of elements than a descendant selector) – Oleg May 24 '12 at 05:56
  • @o.v. awesome answer. This is exactly what I implemented a very good way for my problem to go away! Thank you for this! :) – Abs May 24 '12 at 15:13