del.icio.us .:. tweet

Creating Dynamic Stylesheets .:. kentbrewster.com

Recently I found myself in the position of needing to code a series of syndicated Web badges. To guarantee that they ran as consistently as possible, I started looking for ways to create stylesheets with JavaScript, instead of having to do an additional HTTP request, and--assuming the stylesheet actually loaded--worry about the possibility that my selectors could be overridden by an additional set of rules loaded somewhere else on the page.

To the right should be an example. If you don't see a yellow square with a red outline and the word "woot!" inside, it's not working on your browser. As of right now I see it in IE6, Safari 2, Mozilla 2, and Opera 9 ... if you're using a browser lower than my tested versions and it's working, or it's not and you think it should, please leave a comment with your setup information and what you're seeing.

Documentation in this area is pretty thin on the ground, so here's what worked for me:

// make a new stylesheet
var ns = document.createElement('style');
document.getElementsByTagName('head')[0].appendChild(ns);

// Safari does not see the new stylesheet unless you append something.
// However!  IE will blow chunks, so ... filter it thusly:
if (!window.createPopup) {
   ns.appendChild(document.createTextNode(''));
}
var s = document.styleSheets[document.styleSheets.length - 1];

// some rules to apply
var rules = {
   ".woot" : "{ float: right; margin:10px; background-color:#ff0; border:5px solid #f00; text-align:center; width:100px; }",
   ".woot p" : "{ line-height:100px; margin:auto; color:#00f; font-weight:bold;}"
}

// loop through and insert
for (selector in rules) {
   if (s.insertRule) {
      // it's an IE browser
      try { 
         s.insertRule(selector + rules[selector], s.cssRules.length); 
      } catch(e) {}
   } else {
      // it's a W3C browser
      try { 
         s.addRule(selector, rules[selector]); 
      } catch(e) {}
   }               
}

// find the div you want to futz with
var div = document.getElementById('woot');

// give it the class name
div.className = 'woot';

// build some structure inside
var p = document.createElement('P');
p.innerHTML = 'woot!';
div.appendChild(p);

As always, please feel free to try this out yourself, and let me know how it goes.

Comments from before Disqus:

Kent Brewster .:. 2009-06-03 10:07:36
Thanks, Luke. I need to rewrite this article; the current methods I'm using look like this, where $.d = document and $.w = window:

// create a new stylesheet:
var ns = $.d.createElement('style');
$.d.getElementsByTagName('head')[0].appendChild(ns);

// keep Safari happy
if (!$.w.createPopup) {
ns.appendChild($.d.createTextNode(''));
ns.setAttribute("type", "text/css");
}

// get a pointer to the stylesheet you just created
var sh = $.d.styleSheets[$.d.styleSheets.length - 1];

// rules
var r = {
"h1" : "{color:red;}",
"h2" : "{color:blue;}"
};

// space for IE rule set
var ie = "";

// loop through
for (var s in r) {
// don't accidentally include prototype rules
if (r[s].hasOwnProperty) {
var selector = s;
var rule = r[s];
// create node if not IE
if (!$.w.createPopup) {
var k = $.d.createTextNode(s + r);
ns.appendChild(k);
} else {
ie += s + t;
}
}
}

// is IE? slam it all in at once:
if ($.w.createPopup) {
sh.cssText = ie;
}
Luke Smith .:. 2009-03-20 15:17:11
Each call to addRule or insertRule will trigger a full page reflow. You can avoid that by populating the script with all css content in one go.

var style = d.createElement('style');
style.type = 'text/css';

if (style.styleSheet) {
style.styleSheet.cssText = content;
} else {
style.appendChild(d.createTextNode(content));
}

head.appendChild(style);

Also have a look at the YUI StyleSheet utility for more granular control as well as a fair amount of documention re:gotchas with cross browser dynamic stylesheet stuff (comment block at the end of the module code).
Mario .:. 2008-07-26 06:24:59
I tested it in an older IE version (5.5) an in the Mozilla Navigator 1.7. In both cases the script is working fine. For testing you can also use the website browsershots.org
Nicholas C. Zakas .:. 2007-11-26 09:15:09
I wrote about injecting CSS into a page a little while ago at the YUI Blog: http://yuiblog.com/blog/2007/06/07/style/

Copyright Kent Brewster 1987-2014 .:. FAQ .:. RSS .:. Contact