Add Rules to Stylesheets with JavaScript

Update:Rewrites made to accurately represent current practice for stylesheet injection.

Since we're using so much JavaScript in our web applications these days, we're looking for more ways to keep them fast. We use event delegation to keep event listening efficient, we use function debouncing to limit the number a times a given method can be used, use JavaScript loaders to load only the resources we need, and so on. Another way we can make our pages efficient and fast is to dynamically add and remove styles directly to a stylesheet instead of constantly querying the DOM for elements and applying styles. Here's how it works!

Getting the Stylesheet

Which stylesheet you add the rules to is up to you. If you have a specific stylesheet in mind, you can add an ID to the LINK or STYLE element within your page HTML and get the CSSStyleSheet object by referencing the element's sheet property. The stylesheets can be found in the document.styleSheets object:

One important consideration is the media of the stylesheet -- you want to ensure you aren't adding rules to a print stylesheet when you expect the styles to display on screen. A CSSStyleSheet object does have informational properties for you to peruse:

Unfortunately WebKit requires a hack to properly get things going but all we care about is having that sheet.

Inserting Rules

Stylesheets have an insertRule method which isn't available in earlier IE's but is now the standard for rule injection. The insertRule requires that you write the entire CSS rule just as you would in a stylesheet:

sheet.insertRule("header { float: left; opacity: 0.8; }", 1);

This method may seem a bit ugly for a JavaScript API but that's how it works. The second argument, the index, represents the index at which to insert the rule. This is helpful so that you can insert the same rule/code and define which wins out. The default for index is -1, which means the end of the collection. For extra/lazy control, you may add !important to rules to avoid problems with the index.

Adding Rules - Nonstandard addRule

CSSStyleSheet objects have an addRule method which allows you to register CSS rules within the stylesheet. The addRule method accepts three arguments: the selector, the second the CSS code for the rule, and the third is the zero-based integer index representing the style position (in relation to styles of the same selector):

addRule calls return a result of -1 in all cases -- it really doesn't represent anything.

Remember that the advantage here is that elements added from the page automatically have the styles applied to them; i.e. you wont have to add them to elements as they're injected into the page. Efficient!

Safely Applying Rules

Since browser support for insertRule isn't as global, it's best to create a wrapping function to do the rule application. Here's a quick and dirty method:

This utility method should cover all cases of new style application. If you are nervous about applying variable styles across your app, it's reasonable to wrap the inner code of this method in a try{}catch(e){} block.

Inserting Rules for Media Queries

Media query-specific rules can be added in one of two ways. The first way is through the standard insertRule method:

Of course since IE hasn't always supported insertRule, the other method is creating a STYLE element with the proper media attribute, then adding styles to that new stylesheet. This may require juggling multiple STYLE elements, but that's easy enough. I would probably create an object with media queries as indexes, and create/retrieve them that way.

Dynamically adding rules to stylesheets is efficient and easier than you may think. Keep this strategy in mind on your next big app, as it may save you work in both code and element processing.

This is the hardest thing I've ever had to write, much less admit to myself. I've written resignation letters from jobs I've loved, I've ended relationships, I've failed at a host of tasks, and let myself down in my life. All of those feelings were very...

I love almost every part of being a tech blogger: learning, preaching, bantering, researching. The one part about blogging that I absolutely loathe: dealing with SPAM comments. For the past two years, my blog has registered 8,000+ SPAM comments per day. PER DAY. Bloating my database...

I was recently redesigning my website and wanted to create tooltips. Making that was easy but I also wanted my tooltips to feature the a triangular pointer. I'm a disaster when it comes to images and the prospect of needing to make an image for...

One of the most popular code snippets of posted on my blog has been the scrolling "Go To Top" link snippet. The premise of the snippet is simple: once the user scrolls an element (usually the BODY element) past a given threshold, a "Go...

Discussion

Are there any reasons to why you didnt’t add addCSSRule to the CSSStyleSheet prototype? That way you could skip the first argument and add the rule to the CSS directly. Or if you just added it to “sheet”.

I swear, dealing with style sheet rules is *a pain*!
There has never been consensus on the APIs in the past, essentially because the W3C never stated anything about it. So, as usualy, IE had a different set of APIs, but even among “standard” browsers like Firefox and Opera there were some differences on the way the rules are dealt, grouped and whatever.

Stylesheets have bigger impact on loading times, than they used before, but I don’t think it’s necessary to be so careful about them unless we are developing with some low-spec mobile devices in mind (I would consider optimizing the css selector structure and reduce number of DOM element at first). However, being able to create css rules this way makes creating and editing CSS @keyframes based animations a lot easier (and looks cleaner as well).

* which are a lot more efficient than solutions using requestAnimationFrame() or setInterval(), also we still will have to wait for web animations.

Bryan Forbes

You should check out the interface dgrid has implemented for doing this (which also adds the ability to easily remove added rules and old IE support):

Thanks for the article David. I have found this a useful technique, especially in prototyping. I wrapped this in a library call Csster [http://github.com/ndp/csster]. BTW I ended up moving away from adding individual rules and simply making the browser do all the parsing by inserting a STYLE block… it was just simpler.

1. I’m a little skeptical of the claim that this is efficient, though that really depends on what you’re comparing it to. Dynamically adding rules to a style sheet leads to rerunning selector matching on the entire document, at least in Gecko. Maybe faster than running multiple querySelectorAll calls after anything changes, but that doesn’t mean it’s fast.

Firefox (I have v25) does not have a addRule method for CSSStyleSheet. It instead has a insertRule method (which also exists in Chrome (I tested v31) and IE11. The syntax is different – it’s a full rule string like body { background: red; }

Ok so my code was riddled with mistakes :) 100% correct version, tested on this page and turned the text red:

// get a stylesheet
var styles = document.styleSheets[0];
// we insert an empty rule just to create a new CSSStyleRule object
// the second param is the index to insert at
// using the length property we effectively "append" the rule to the end of the sheet
var ruleIndex = styles.insertRule("p {}", styles.cssRules.length)
// the index is the position the rule was inserted at, so we can now get a handle on it
var rule = styles.cssRules.item(ruleIndex);
// now we have it, modify it's styles!
// applied to the document immediately
rule.style.color = "red";

Some years ago I got all excited about the performance implications of modifying the CSS rules and leaning on the browser’s internal machinery rather than doing lots of DOM queries and setting style properties in js. And in some cases it does turn out to be faster – table column widths was my canonical example. But when I went to write this up and did more cross-browser/platform testing to confirm my findings I found the results wildly variable – probably due to the issues dbaron points out: “Dynamically adding rules to a style sheet leads to rerunning selector matching on the entire document, at least in Gecko”

The upshot wasnt nearly as exciting as I hoped: it’s sometimes faster in some specific cases, in particular environments. YMMV and measure don’t guess.

That said, as far as patterns go, making presentation changes across a document by changing the stylesheet seems preferable to querying and manipulating style properties on the elements themselves. Maybe this rerunning selector matching on the entire document issue should be filed as a bug? It seems heavy-handed and like we could do a bit better. And a rudimentary selector API for CSSOM (find rules efficiently) wouldn’t hurt too.

I noticed that when I tried using the code in this article, the Webkit hack was causing issues in IE but removing it didn’t seem to cause any issues. Can you remember which versions of Webkit were affected and what happens if the hack isn’t included?

A little improvement to the original addCSSRule function: Basically, it does not add additional cssRules for the same selector, instead it appends the rules to the existing one, and it returns the resulting cssRule.

One thing worth noting about dealing with adding rules to the stylesheet: Prefixed properties may need special handling depending on your use-case.

If you add a rule with a property which uses a prefix like -webkit, -moz or whatever, all the non-supported ones will get stripped out. So for example, you could add a -moz prefixed property in Webkit, and it will simply strip it out.

Thus, if you actually want to for example save the CSS you’ve generated for whatever reason, you’ll need to use some other approach to include the prefixed versions for other browsers.

Good point!
But when I test the performance in the Chrome timeline, I found that when using insertStyle/deleteStyle, it makes the Recalculate Style time more longer than change style directly on the DOM or change the classes, about 1ms to 0.3ms respectively in my simple test.

And I look inside what’s going on when the browser is recalculating the style, I found that when I edit the style rule in the styleSheet, it forces the browser to recaculate all the style rules, and the worst thing is this performance gap is growing when my CSS rule is getting more complex.

If there’s some image that is base64 format, the browser need to parse all that images again, which makes it more slower. In my another test, which I have lots base64 images, the results of Recaculate Style time becomes 5ms to 0.3ms respectively.

Great article, it’s helped me crack the back of a persistent problem I’ve had on a big web app build.

For my usage I needed to clear all the CSS rules each time I was going to update them so I decided to make a function to handle this. It might come in handy for anyone who wants to delete / remove rules.

I think it’s worth saying that dynamically changing stylesheets this way may lead to some devtools weirdness.
For instance, in chrome, open a page that runs JS to update its own stylesheet. Then open the chromedevtools and select an element that has a CSS rule that was just changed.
You won’t see the changes in the Styles tab, instead you’ll see the unaltered, as-authored style.

The thing is it’s pretty important for browser devtools to show the styles as they have been authored. And dynamic updates to the CSS object-model aren’t easily reconciled with the original as-authored stylesheet.

Right now, firefoxdevtools does *not* show the styles as they were authored, so this scenario will work.

But why not use the Modernizr-approach by setting a class on html or body to set various things to how you want them. It seems a lot simpler to me and doesn’t require you to do any css hacking in the first place.

And i also like to suggest to use normal mediaqueries in the first place. If your content doesn’t do that right than you might think about doing these dirty solutions but i still don’t see why you would ever need any of this. It just seems like your classes are all wrong

I wrote a small JS Perf, and as I was suspecting this is extremely unlikely to perform in any situation, manipulating the DOM directly is still the best thing to do (or even better just swap CSS classes)http://jsperf.com/stylesheet-vs-dom-manipulation

In what situations would adding a new stylesheet be better than manipulating classes with JS?

Mozilla says: “Add a stylesheet rule to the document (may be better practice, however,
* to dynamically change classes, so style information can be kept in
* genuine stylesheets (and avoid adding extra elements to the DOM))”