Invalid procedure call or argument IE issue when iterating through document.styleSheets using $.each()
-
21-09-2019 - |
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.
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);
}
});