6

I am unable to query cssRules if @import is present in css stylesheet. Is it expected as per web standard? Or it's know limitation of Firefox?

Note: I am importing css file from same domain.

var style_rules = document.styleSheets[0].cssRules;
console.log(style_rules);

A parameter or an operation is not supported by the underlying object [Break On This Error] var style_rules = document.styleSheets[0].cssRules;

P K
  • 9,972
  • 12
  • 53
  • 99
  • 1
    possible duplicate of [Firefox not able to enumerate document.styleSheets\[\].cssRules\[\]](http://stackoverflow.com/questions/5323604/firefox-not-able-to-enumerate-document-stylesheets-cssrules) // (edit) err, I can't read, it's not a duplicate, but that link still might be useful for the future readers. – Nickolay Nov 26 '11 at 00:47

3 Answers3

3

The property document.styleSheets[0].cssRules is a CSSRuleList (apart from in IE6-8, where you should styleSheets[0].rules for the css rules and styleSheets[0].imports for the imports). This CSSRuleList has a certain number of CSSRules in the list. These rules can be of different types. For instance a @import CSSRule implements the CSSImportRule interface and a 'normal' style declaration CSSRule implements the CSSStyleRule interface. We can detect if a CSSRule is a @import css rule, by checking if rule.type == IMPORT_RULE, where IMPORT_RULE is 3. If it is a CSSImportRule, we can obtain its styleSheet property to obtain the css rules in the imported stylesheet and repeat the process described above.

The parsable textual representation of any CSSRule can be obtained by obtaining the cssText property: rule.cssText. In Internet Explorer 6-8 however, we have to use rule.style.cssText.

In other words, we can obtain all CSSRule's in the stylesheets (including its imports) using the following code. I've also put a working sample into a jsfiddle. Note that this code does not work in IE6-8. For this solution I suggest you to check my solution for another problem on SO here.

/**
 * Get the css rules of a stylesheet which apply to the htmlNode. Meaning its class
 * its id and its tag.
 * @param CSSStyleSheet styleSheet
 */
function getCssRules(styleSheet) {
    if ( !styleSheet )
        return null;

    var cssRules = new Array();
    if (styleSheet.cssRules) {
        var currentCssRules = styleSheet.cssRules;
        // Import statement are always at the top of the css file.
        for ( var i = 0; i < currentCssRules.length; i++ ) {
            // cssRules contains the import statements.
            // check if the rule is an import rule.
            if ( currentCssRules[i].type == 3 ) {
                // import the rules from the imported css file.
                var importCssRules = getCssRules(currentCssRules[i].styleSheet);
                if ( importCssRules != null ) {
                    // Add the rules from the import css file to the list of css rules.
                    cssRules = addToArray(cssRules, importCssRules);
                }
                // Remove the import css rule from the css rules.
                styleSheet.deleteRule(i);
            }
            else {
                // We found a rule that is not an CSSImportRule
                break;
            }
        }
        // After adding the import rules (lower priority than those in the current stylesheet),
        // add the rules in the current stylesheet.
        cssRules = addToArray(cssRules, currentCssRules);
    }


    return cssRules;
}

/**
 * Since a list of rules is returned, we cannot use concat. 
 * Just use old good push....
 * @param CSSRuleList cssRules
 * @param CSSRuleList cssRules
 */
function addToArray(cssRules, newRules) {
    for ( var i = 0; i < newRules.length; i++ ) {
        cssRules.push(newRules[i]);
    }
    return cssRules;
}

/**
 * Finds all CSS rules.
 */
function getCSSRules() {
    var cssRules = new Array();

    // Loop through the stylesheets in the html document.
    for ( var i = 0; i < document.styleSheets.length; i++ ) {
        var currentCssRules = getCssRules(document.styleSheets[i])
        if ( currentCssRules != null )
            cssRules.push.apply(cssRules, currentCssRules);
    }

    return cssRules;
}

// An array of all CSSRules.
var allCSSRules = getCSSRules();
    for ( var i = 0; i < allCSSRules.length; i++ ) {
        console.log(allCSSRules[i].cssText);
    }
Community
  • 1
  • 1
dennisg
  • 4,358
  • 1
  • 23
  • 28
0

Seems to be working fine, although I only tested it with nonexistant files. jsfiddle example working on Firefox 8

update 1: I think I found out why you had a problem. If the cssRule is an import you have to parse the cssRules of its StyleSheet property instead. This was quite easy to spot with the Firebug Addon for Firefox.

here is the test.html

<html>
<head>
<style type="text/css">
@import 'test.css';
</style>
</head>
<body>
<div class="random5923">showing with border</div>
<script type="text/javascript">
console.log(document.styleSheets);
console.log(document.styleSheets[0].cssRules[0].styleSheet.cssRules[0].cssText);
</script>
</body>
</html>

and the corresponding test.css

.random5923 {
    border : 1px solid black;
}

update 2: According to http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSImportRule the CSSImportRule Object definitively has a styleSheet property. Please use my example to check this for yourself, but keep this in mind:

The value of this attribute is null if the style sheet has not yet been loaded

You may want to delay your code until this property has been loaded.

  • I don't think CSSRule object has styleSheet property. It has only style and selectorText property. – P K Nov 30 '11 at 15:09
-1

Dennis and Friedrich need to take a course in reading comprehension. If document.styleSheets[0].cssRules fails then obviously their code will also fail at if (styleSheet.cssRules) { and console.log(document.styleSheets[0].cssRules[0].styleSheet.cssRules[0].cssText); respectively. Seriously, what's the use in posting an answer you put zero thought into?

With that out of the way, my guest guess based on the limited information you provide is that you are getting a DOMException ‘InvalidAccessError’ because the stylesheet hasn't been completely transferred from the server to your computer yet when the JavaScript code runs.

If this is the case, you'll need to handle the load event for the HTML element that owns the stylesheet.

try
{
  var rules = styleSheet.cssRules;
}
catch(x)
{
  if(x instanceof DOMException && x.name == "InvalidAccessError")
    styleSheet.ownerNode.addEventListener("load", functionToRunWhenItLoads);
}

You can adapt the above code for your own purposes. Take care however that some browsers don't even add the style sheet to document.styleSheets until the style sheet finishes loading, so you might still miss it that way. You may need to handle the load event of the window object to deal with that case, or perhaps it's better not to go through document.styleSheets in the first place.

Anonymous
  • 57
  • 2