Question

I've written this code that iterates over all global style sheet rules and stores them in an array/object. I use this dictionary-like object later to change global rules rather than setting styles on individual elements.

Following code breaks in IE8 but works fine in Firefox3.7 and Chrome4.

var allRules;

$(function() {
    var fileRules;
    allRules = [];
    $.each(document.styleSheets, function() {
        // get rules for any browser (IE uses rules array)
        fileRules = this.cssRules || this.rules;
        $.each(fileRules, function() {
            allRules[this.selectorText] = this;
        });
    });
});

I get Invalid procedure call or argument error. When I try to debug it, this code sucessfully iterates through two CSS style sheet files with rules but when the second one's iteration is done, it fails.

I can't seem to find an error in this code.

Was it helpful?

Solution

The problem

After thorough testing I found out that document.styleSheets isn't a regular array in IE. That's why it breaks in $.each() call when it reaches the end.

If we take a look at jQuery function itself it has a for loop to iterate over an object that has a length property, falsely believing it's an array. document.styleSheets does have length property, but it's obviously not an array. So when this for loop in $.each() is executed:

for (var value = object[0];
     i < length && callback.call( value, i, value ) !== false;
     value = object[++i]){}

it fails after the last element has been iterated over. As we may see this for loop doesn't increment i on its own but rather increments it while assigning a new value to value.

We can check this manually as well. Write these two lines in any browser's address bar:

javascript:var a=[1,2,3];alert(a[3]);void(0);
javascript:alert(document.styleSheets[document.styleSheets.length]);void(0);

The first one runs fine in all browsers, but the second one fails in IE.

The solution

We have to rewrite the iteration over style sheets

var allRules;

$(function() {
    var fileRules;
    allRules = {};
    // can't use $.each() over document.styleSheets because it's not an array in IE
    for (var i = 0; i < document.styleSheets.length; i++)
    {
        fileRules = document.styleSheets[i].cssRules || document.styleSheets[i].rules;
        $.each(fileRules, function() {
            allRules[this.selectorText] = this;
        });
    }
});

OTHER TIPS

Could it be that parsing rule itself is failing? Try experimenting with different stylesheets and reorder the rules to ensure that there isn't a problem parsing the rule for some reason.

code true is:

var fileRules;
(function ($) {
    allRules = {};
    for (var i = 0; i < document.styleSheets.length; i++) {
        fileRules = document.styleSheets[i].cssRules || document.styleSheets[i].rules;
        $.each(fileRules, function () {
            allRules[this.selectorText] = this;
        })(jQuery);
    }
});
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top