views:

279

answers:

4
+1  Q: 

CSSRules is empty

I have this very simple HTML page, and I'm trying to get the CSSRules of #poulet, but when I'm accessing the documents.styleSheets[0].cssRules I get this error in Chrome v5.0.375.55:
Uncaught TypeError: Cannot read property 'length' of null

Here is what my code looks like:

HTML FILE

<!DOCTYPE html> 
<html lang="en"> 
<head>
<link rel="stylesheet" href="css/test.css" type="text/css" />
<script type="text/javascript" src="js/test.js"></script>
<title>Mozilla</title>
<script>
window.onload = function(){
    var test = findKeyframesRule('poulet');
    alert(test);
}
</script>
</head>
<body>
    <div id="poulet">
        allo
    </div>
</body>
</html>

JS FILE

function findKeyframesRule(rule)
{
    var ss = document.styleSheets;

    for (var i = 0; i < ss.length; ++i)
    {       
        for (var j = 0; j < ss[i].cssRules.length; ++j)
        {
            if (ss[i].cssRules[j].type == window.CSSRule.WEBKIT_KEYFRAMES_RULE && ss[i].cssRules[j].name == rule)
                return ss[i].cssRules[j];
        }
    }
    return null;
}

CSS FILE

html, body {
    background: #cccccc;
}

#poulet{
    border: 10px solid pink;
}

The files can be found here. I really need help on this one, please!!! D:

A: 

Chrome uses the rules[] array like IE.

document.styleSheets[0].rules.length

So modify your loop:

for (var i = 0; i < ss.length; ++i)
{       
    var rules = ss[i].cssRules;
    if (!rules)
        rules = ss[i].rules;

    for (var j = 0; j < rules.length; ++j)
    {
        if (rules[j].type == window.CSSRule.WEBKIT_KEYFRAMES_RULE && rules[j].name == rule)
            return rules[j];
    }
}
Joel Potter
Just one remark: for optimization purposes, you could use a decrementing `while` loop, see http://blogs.sun.com/greimer/entry/best_way_to_code_a
Marcel Korpel
Still not working even with the loop modified. Plus, I used the same code as from a webkit nightly version here: http://gitorious.org/webkit/webkit/commit/438fd0b118bd9c2c82b6ab23956447be9c24f136 where is clearly uses .cssRules instead of .rules. I have this warning too, I think it's what's causing me all this trouble: Resource interpreted as script but transferred with MIME type application/x-js.
Stephanie
As of Chrome 5, Chrome uses both `rules` and `cssRules`.
Marcel Korpel
A: 

You can learn about diferent browsers interpretation of cssrules here:

.cssRules and .rules

Thus, it has your answer :)

Zuul
That's not very up-to-date: “Page last changed 3 years ago”. Chrome/Chromium isn't mentioned [there](http://www.quirksmode.org/dom/changess.html#link3) at all.
Marcel Korpel
You're right, but it's a steep forward never the less :)
Zuul
+1  A: 

I found these errors in your script:

  1. You're testing for type being equal to window.CSSRule.WEBKIT_KEYFRAMES_RULE. That property has a value of 8, whereas the desired object has a type of 1. A quick look in the CSSRule object reveals that you probably want to compare with window.CSSRule.STYLE_RULE.

  2. I couldn't find a property name, but in the end found a property selectorText containing #poulet.

After corrections the script reads:

function findKeyframesRule(rule)
{
    var ss = document.styleSheets;

    for (var i = 0; i < ss.length; ++i)
    {
        for (var j = 0; j < ss[i].cssRules.length; ++j)
        {
            if (ss[i].cssRules[j].type == window.CSSRule.STYLE_RULE && ss[i].cssRules[j].selectorText == '#'+rule)
                return ss[i].cssRules[j];
        }
    }
    return null;
}

But beware of the problem I mentioned in a comment: this only works when protocol != "file:",

Marcel Korpel
Thanks, that's very helpful! Though in my case I need window.CSSRule.WEBKIT_KEYFRAMES_RULE, I now have a better understanding of how to get the rules now. Do you know why exactly it's not working when running for file protocol?
Stephanie
@Stephanie: I don't understand: why do you need WEBKIT_KEYFRAMES_RULE? Doing so will let the function return `null` instead of the CSSStyleRule object I think you want. Moreover, this way it doesn't work cross-browser, whereas using STYLE_RULE let this script work in Firefox. Unfortunately, there are more strange problems when using the file protocol. It seems that local access is restricted (http://code.google.com/p/chromium/issues/detail?id=44400). I'll look a bit deeper into this within a few days and file a bug report, if appropiate.
Marcel Korpel
@Marcel: The script I posted here is only a sample of what I'm actually doing. The whole purpose is to change the animation keyframes - nothing else - using javascript and this script is intended for iphone/ipad/ipod touch, all using safari which is webkit-based. Therefore I assume it's (performance-wise) wiser to use WEBKIT_KEYFRAMES_RULE in this situation. Though I have admit STYLE_RULE would probably be better for a function that can be used more widely.
Stephanie
+1  A: 

This method returns an array of rules that contain a given selector-

to work in correctly IE, the selector test has to be case insensitive.

Chrome 5 will work with either rules or cssRules, by the way.

function findRule(rule){
    var ss= document.styleSheets, L= ss.length, A= [], R, RL, ru;
    var rx= RegExp('\\b'+rule+'\\b','i');
    for(var i= 0; i < L; ++i){
        R= ss[i].cssRules || ss[i].rules,
        RL= R.length;
        while(RL){
            ru= R[--RL];
            if(rx.test(ru.selectorText)){
                A.unshift(ru.selectorText+'{'+ru.style.cssText+'}');
            }
        }
    }
    return A;
}

alert(findRule('body').join('\n\n'))

Note-may be of use to you-

If I run this in a directory on my c-drive I can't read any styleSheet's length or rules unless they are in the same directory as the page.

It runs fine when served via http- from localhost or the internet.

kennebec
Regarding `file://` vs. `http://`: I noticed that, too, see my 2nd comment on the original question. I'll look a bit deeper into this within a few days and file a bug report, if appropiate.
Marcel Korpel