Find all CSS rules that apply to an element.

Many tools / APIs provide ways to select elements of specific classes or identifiers. You can also check the browser loaded source stylesheets.

However, in order for browsers to display the element, they collect all CSS rules (possibly from different stylesheets) and apply it to the element. This is what you see with Firebug or WebKit Inspector - the complete CSS inheritance tree for an element.

How can I reproduce this function in pure JavaScript without requiring additional browser plugins?

Perhaps the example may provide some clarification on what I'm looking for:

<style type="text/css"> p { color :red; } #description { font-size: 20px; } </style> <p id="description">Lorem ipsum</p> 

Here, the p # description element has two CSS rules: red and a font size of 20 px.

I would like to find the source where these computed CSS rules come from (color enters the p rule, etc.).

+55
javascript css
Jun 01 '10 at 19:30
source share
7 answers

I seem to be able to answer my question after another hour of research.

It is so simple:

 window.getMatchedCSSRules(document.getElementById("description")) 

(Works in WebKit / Chrome, possibly with others)

+20
Jun 01 '10 at 20:33
source share

Since this question does not currently have an easy (non-library) cross-browser compatible answer, I will try to provide one:

 function css(el) { var sheets = document.styleSheets, ret = []; el.matches = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector || el.oMatchesSelector; for (var i in sheets) { var rules = sheets[i].rules || sheets[i].cssRules; for (var r in rules) { if (el.matches(rules[r].selectorText)) { ret.push(rules[r].cssText); } } } return ret; } 

JSFiddle: http://jsfiddle.net/HP326/6/

The css(document.getElementById('elementId')) call css(document.getElementById('elementId')) returns an array with an element for each CSS rule that corresponds to the passed element. If you want to know more specific information about each rule, check out the CSSRule documentation.

+49
Mar 25 '14 at 15:01
source share

Take a look at this library that does what was suggested: http://www.brothercake.com/site/resources/scripts/cssutilities/

It works in all modern browsers directly in IE6, can provide you with collections of rules and properties, such as Firebug (in fact, it is more accurate than Firebug), and can also calculate the relative or absolute specificity of any rule. The only caveat is that although he understands the static types of media, he does not understand media queries.

+15
Aug 18 2018-12-18T00:
source share

Short version April 12, 2017

Challenger will appear.

 var getMatchedCSSRules = (el, css = el.ownerDocument.styleSheets) => [].concat(...[...css].map(s => [...s.cssRules||[]])) /* 1 */ .filter(r => el.matches(r.selectorText)); /* 2 */ 

Line /* 1 */ builds a flat array of all the rules.
The line /* 2 */ cancels the rules of non-compliance.

Based on function css(el) by @SB on the same page.

Example 1

 var div = iframedoc.querySelector("#myelement"); var rules = getMatchedCSSRules(div, iframedoc.styleSheets); console.log(rules[0].parentStyleSheet.ownerNode, rules[0].cssText); 

Example 2

 var getMatchedCSSRules = (el, css = el.ownerDocument.styleSheets) => [].concat(...[...css].map(s => [...s.cssRules||[]])) .filter(r => el.matches(r.selectorText)); function Go(big,show) { var r = getMatchedCSSRules(big); PrintInfo: var f = (dd,rr,ee="\n") => dd + rr.cssText.slice(0,50) + ee; show.value += "--------------- Rules: ----------------\n"; show.value += f("Rule 1: ", r[0]); show.value += f("Rule 2: ", r[1]); show.value += f("Inline: ", big.style); show.value += f("Computed: ", getComputedStyle(big), "(…)\n"); show.value += "-------- Style element (HTML): --------\n"; show.value += r[0].parentStyleSheet.ownerNode.outerHTML; } Go(...document.querySelectorAll("#big,#show")); 
 .red {color: red;} #big {font-size: 20px;} 
 <h3 id="big" class="red" style="margin: 0">Lorem ipsum</h3> <textarea id="show" cols="70" rows="10"></textarea> 

Omissions

  • No multimedia processing, no @import , @media .
  • There is no access to styles loaded from cross-domain style sheets.
  • There is no sorting by the "specificity" of the selector (order of importance).
  • No styles inherited from parents.
  • May not work with old or rudimentary browsers.
  • Not sure how he handles pseudo-classes and pseudo-selectors, but everything seems to be in order.

Perhaps one day I will consider these shortcomings.

Long version May 4, 2017

There is a much more complete implementation, taken from the someones GitHub page (the original code forked from this, via Bugzilla ), written for Gecko and IE, but it is rumored to also work with Blink.

May 4, 2017: The specificity calculator had critical errors, which I have now fixed. (I cannot notify the authors because I do not have a GitHub account.)

 // polyfill window.getMatchedCSSRules() in FireFox 6+ if (typeof window.getMatchedCSSRules !== 'function') { var ELEMENT_RE = /[\w-]+/g, ID_RE = /#[\w-]+/g, CLASS_RE = /\.[\w-]+/g, ATTR_RE = /\[[^\]]+\]/g, // :not() pseudo-class does not add to specificity, but its content does as if it was outside it PSEUDO_CLASSES_RE = /\:(?!not)[\w-]+(\(.*\))?/g, PSEUDO_ELEMENTS_RE = /\:\:?(after|before|first-letter|first-line|selection)/g; // convert an array-like object to array function toArray(list) { return [].slice.call(list); } // handles extraction of `cssRules` as an `Array` from a stylesheet or something that behaves the same function getSheetRules(stylesheet) { var sheet_media = stylesheet.media && stylesheet.media.mediaText; // if this sheet is disabled skip it if ( stylesheet.disabled ) return []; // if this sheet media is specified and doesn't match the viewport then skip it if ( sheet_media && sheet_media.length && ! window.matchMedia(sheet_media).matches ) return []; // get the style rules of this sheet return toArray(stylesheet.cssRules); } function _find(string, re) { var matches = string.match(re); return matches ? matches.length : 0; } // calculates the specificity of a given `selector` function calculateScore(selector) { var score = [0,0,0], parts = selector.split(' '), part, match; //TODO: clean the ':not' part since the last ELEMENT_RE will pick it up while (part = parts.shift(), typeof part == 'string') { // find all pseudo-elements match = _find(part, PSEUDO_ELEMENTS_RE); score[2] += match; // and remove them match && (part = part.replace(PSEUDO_ELEMENTS_RE, '')); // find all pseudo-classes match = _find(part, PSEUDO_CLASSES_RE); score[1] += match; // and remove them match && (part = part.replace(PSEUDO_CLASSES_RE, '')); // find all attributes match = _find(part, ATTR_RE); score[1] += match; // and remove them match && (part = part.replace(ATTR_RE, '')); // find all IDs match = _find(part, ID_RE); score[0] += match; // and remove them match && (part = part.replace(ID_RE, '')); // find all classes match = _find(part, CLASS_RE); score[1] += match; // and remove them match && (part = part.replace(CLASS_RE, '')); // find all elements score[2] += _find(part, ELEMENT_RE); } return parseInt(score.join(''), 10); } // returns the heights possible specificity score an element can get from a give rule selectorText function getSpecificityScore(element, selector_text) { var selectors = selector_text.split(','), selector, score, result = 0; while (selector = selectors.shift()) { if (matchesSelector(element, selector)) { score = calculateScore(selector); result = score > result ? score : result; } } return result; } function sortBySpecificity(element, rules) { // comparing function that sorts CSSStyleRules according to specificity of their `selectorText` function compareSpecificity (a, b) { return getSpecificityScore(element, b.selectorText) - getSpecificityScore(element, a.selectorText); } return rules.sort(compareSpecificity); } // Find correct matchesSelector impl function matchesSelector(el, selector) { var matcher = el.matchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector || el.msMatchesSelector; return matcher(selector); } //TODO: not supporting 2nd argument for selecting pseudo elements //TODO: not supporting 3rd argument for checking author style sheets only window.getMatchedCSSRules = function (element /*, pseudo, author_only*/) { var style_sheets, sheet, sheet_media, rules, rule, result = []; // get stylesheets and convert to a regular Array style_sheets = toArray(window.document.styleSheets); // assuming the browser hands us stylesheets in order of appearance // we iterate them from the beginning to follow proper cascade order while (sheet = style_sheets.shift()) { // get the style rules of this sheet rules = getSheetRules(sheet); // loop the rules in order of appearance while (rule = rules.shift()) { // if this is an @import rule if (rule.styleSheet) { // insert the imported stylesheet rules at the beginning of this stylesheet rules rules = getSheetRules(rule.styleSheet).concat(rules); // and skip this rule continue; } // if there no stylesheet attribute BUT there IS a media attribute it a media rule else if (rule.media) { // insert the contained rules of this media rule to the beginning of this stylesheet rules rules = getSheetRules(rule).concat(rules); // and skip it continue } // check if this element matches this rule selector if (matchesSelector(element, rule.selectorText)) { // push the rule to the results set result.push(rule); } } } // sort according to specificity return sortBySpecificity(element, result); }; } 

Bugs fixed

  • = match β†’ += match
  • return re ? re.length : 0; β†’ return matches ? matches.length : 0; return matches ? matches.length : 0;
  • _matchesSelector(element, selector) β†’ matchesSelector(element, selector)
+12
Jun 22 '16 at 3:58
source share

Here's the SB version of the response, which also returns the corresponding rules in matching media queries. I removed the merge *.rules || *.cssRules *.rules || *.cssRules and search .matches ; add polyfill or add these lines if you need them.

This version also returns CSSStyleRule objects, not the rule text. I think this is a little more useful, since the specifics of the rules can be more easily diagnosed programmatically this way.

Coffee

 getMatchedCSSRules = (element) -> sheets = document.styleSheets matching = [] loopRules = (rules) -> for rule in rules if rule instanceof CSSMediaRule if window.matchMedia(rule.conditionText).matches loopRules rule.cssRules else if rule instanceof CSSStyleRule if element.matches rule.selectorText matching.push rule return loopRules sheet.cssRules for sheet in sheets return matching 

JS:

 function getMatchedCSSRules(element) { var i, len, matching = [], sheets = document.styleSheets; function loopRules(rules) { var i, len, rule; for (i = 0, len = rules.length; i < len; i++) { rule = rules[i]; if (rule instanceof CSSMediaRule) { if (window.matchMedia(rule.conditionText).matches) { loopRules(rule.cssRules); } } else if (rule instanceof CSSStyleRule) { if (element.matches(rule.selectorText)) { matching.push(rule); } } } }; for (i = 0, len = sheets.length; i < len; i++) { loopRules(sheets[i].cssRules); } return matching; } 
+3
Mar 21 '16 at 8:43
source share

 var GetMatchedCSSRules = (elem, css = document.styleSheets) => Array.from(css) .map(s => Array.from(s.cssRules).filter(r => elem.matches(r.selectorText))) .reduce((a,b) => a.concat(b)); function Go(paragraph, print) { var rules = GetMatchedCSSRules(paragraph); PrintInfo: print.value += "Rule 1: " + rules[0].cssText + "\n"; print.value += "Rule 2: " + rules[1].cssText + "\n\n"; print.value += rules[0].parentStyleSheet.ownerNode.outerHTML; } Go(document.getElementById("description"), document.getElementById("print")); 
 p {color: red;} #description {font-size: 20px;} 
 <p id="description">Lorem ipsum</p> <textarea id="print" cols="50" rows="12"></textarea> 
+1
Sep 29 '16 at 14:18
source share

Providing IE9 +, I wrote a function that calculates CSS for the requested element and its children and makes it possible to save it in a new className, if necessary in the fragment below.

 /** * @function getElementStyles * * Computes all CSS for requested HTMLElement and its child nodes and applies to dummy class * * @param {HTMLElement} element * @param {string} className (optional) * @param {string} extras (optional) * @return {string} CSS Styles */ function getElementStyles(element, className, addOnCSS) { if (element.nodeType !== 1) { return; } var styles = ''; var children = element.getElementsByTagName('*'); className = className || '.' + element.className.replace(/^| /g, '.'); addOnCSS = addOnCSS || ''; styles += className + '{' + (window.getComputedStyle(element, null).cssText + addOnCSS) + '}'; for (var j = 0; j < children.length; j++) { if (children[j].className) { var childClassName = '.' + children[j].className.replace(/^| /g, '.'); styles += ' ' + className + '>' + childClassName + '{' + window.getComputedStyle(children[j], null).cssText + '}'; } } return styles; } 

Using

 getElementStyles(document.getElementByClassName('.my-class'), '.dummy-class', 'width:100%;opaity:0.5;transform:scale(1.5);'); 
0
Mar 16 '17 at 20:25
source share



All Articles