Reputation: 561
Apparently there is an issue with the property document.styleSheets in Google Chrome. The property is not updated after a new stylesheet is added. To reproduce the issue simply run the following code:
// Create the link element
var element = document.createElement("link");
element.setAttribute("rel", "stylesheet");
element.setAttribute("type", "text/css");
element.setAttribute("href", "external.css");
// Add the link element
console.log('Before add: ' + document.styleSheets.length);
document.getElementsByTagName("head")[0].appendChild(link);
console.log('After add: ' + document.styleSheets.length);
When running in FireFox the console will show something like:
Before add: 4
After add: 5
When running in Chrome the console will show something like:
Before add: 4
After add: 4
Interestingly, the following code works correctly (return the same results) on both browsers:
console.log('Before add: ' + document.getElementsByTagName("head")[0].children);
document.getElementsByTagName("head")[0].appendChild(link);
console.log('After add: ' + document.getElementsByTagName("head")[0].children);
Is this a bug or I'm doing something wrong?
I discovered this issue while writing and testing the following function to dynamically add CSS files to a web page:
function requiresCSS( name ) {
var sheets = document.styleSheets;
for (var i = 0; i < sheets.length; i++) {
if (sheets[i].href && sheets[i].href.indexOf(name) !== -1) {
return;
}
}
var link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = name;
document.getElementsByTagName("head")[0].appendChild(link);
}
When trying to add CSS twice, the function works perfectly on Firefox but fails on Chrome.
Upvotes: 0
Views: 683
Reputation: 1074385
You're adding an external file and then checking for it immediately. It's not surprising that you wouldn't see it yet, the HTTP request is probably still outstanding. If you check back later, once the HTTP request is complete and the styles parsed and applied, you'll see it in document.styleSheets
.
According to the HTML5 specification, you can use the load
event on the link element to get a notification of when the resource has been loaded. So:
element.addEventListener("load", function() {
console.log('After add: ' + document.styleSheets.length);
}, false);
console.log('Before add: ' + document.styleSheets.length);
document.getElementsByTagName("head")[0].appendChild(link);
Of course, loading and parsing/applying may not be simultaneous, so if you still see a disparity, you may need a setTimeout
in there:
element.addEventListener("load", function() {
setTimeout(function() {
console.log('After add: ' + document.styleSheets.length);
}, 0);
}, false);
console.log('Before add: ' + document.styleSheets.length);
document.getElementsByTagName("head")[0].appendChild(link);
Re your function that tries to avoid adding a stylesheet twice, since the element is there reliably even if the entry in document.styleSheets
isn't, I'd just look for the element:
function requiresCSS( name ) {
// If we already have an element for this stylesheet, return
if (document.querySelector('link[rel=stylesheet][href="' + name + '"]') {
return;
}
// Add it
var link = document.createElement("link"); link.rel = "stylesheet";
link.type = "text/css"; link.href = name;
document.getElementsByTagName("head")[0].appendChild(link);
}
(You could also use querySelector
for finding head
, it'd be a bit more concise. querySelector
/ querySelectorAll
are supported on all modern browsers, and also IE8.)
Upvotes: 5