Skip to content

Instantly share code, notes, and snippets.

@getify
Created June 19, 2012 21:47
Show Gist options
  • Save getify/2956735 to your computer and use it in GitHub Desktop.
Save getify/2956735 to your computer and use it in GitHub Desktop.
implementing CSS3 specificity algorithm (with some extensions) in JS
/**
* calculates a set of specificity scores for a CSS selector
* based on (but extending) the CSS3 specificity algorithm:
* http://reference.sitepoint.com/css/specificity
*
* @param {string} selector
*/
function specificity(selector) {
var specificity = [0,0,0,0,0], idx, matches, tmp = 0,
selectors = selector.split(/\s*,\s*/)
;
for (idx in selectors) {
// id selectors
matches = selectors[idx].match(/#/g);
if (matches) specificity[1] += matches.length;
// class selectors
matches = selectors[idx].match(/\./g);
if (matches) specificity[2] += matches.length;
// attribute selectors
matches = selectors[idx].match(/\[.+\]/g);
if (matches) specificity[2] += matches.length;
// pseudo-element selectors
matches = selectors[idx].match(/:(?:first-letter|first-line|before|after|:selection)/g);
if (matches) {
tmp = matches.length;
specificity[3] += tmp;
}
// pseudo-(element and class) selectors
matches = selectors[idx].match(/:/g);
if (matches) specificity[2] += (matches.length - tmp); // filter out the count of lower-precedence matches of pseudo-elements
tmp = 0;
// child, adjacent-sibling, and general-sibling combinators (Note: extension to CSS3 algorithm)
matches = selectors[idx].match(/[+>~]/g);
if (matches) specificity[4] += matches.length;
// element type selectors (note: must remove everything else to find this count)
selectors[idx] = selectors[idx]
.replace(/\(.*?\)/g,"")
.replace(/\[.*?\]/g,"")
.replace(/[.#:][^ +>~]*/ig,"");
matches = selectors[idx].match(/[^ +>~]+/g);
if (matches) specificity[3] += matches.length;
}
return specificity;
};
// Note #1: the fifth score in the matrix is for the > ~ and + combinators, one of my extensions to the CSS3 specificity algorithm
// Note #2: I made multiple rules (separated by ,) additive, as I think that also makes for a more specific rule, as opposed to two separate rules). See the first and second examples below.
specificity("div") // [0, 0, 0, 1, 0]
specificity("div, span") // [0, 0, 0, 2, 0]
specificity("div#foo") // [0, 1, 0, 1, 0]
specificity("div#foo .blah") // [0, 1, 1, 1, 0]
specificity("div#foo > .blah") // [0, 1, 1, 1, 1]
specificity("div#foo > .blah table") // [0, 1, 1, 2, 1]
specificity("div#foo > .blah ~ table") // [0, 1, 1, 2, 2]
specificity("div#foo > .blah ~ table.blah.blah2") // [0, 1, 3, 2, 2]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment