Playing with Stylesheets

CSS JavaScript

I’m sure most of you are familiar with styling DOM elements using CSS selectors. But what if you wanted to create your own stylesheets on the fly or perhaps query or alter existing CSS rules on your page? That’s a little trickier, but also a bit of fun so let’s take a crack at it.

But first let’s look at why we might want to do this. One example might be a theme editor for a content management system that lets the user dynamically change element styles. However instead of updating those styles on individual elements one by one, the editor could dynamically change the CSS rules and let the browser do the work for it.

Check out the pen below where I’ve whipped up a quick demonstration of how this might work.

See the Pen lsmLd by Simon Collins (@devilish) on CodePen.

A more complex example can be found in my Google Chrome extension Seesponsive which shows you the media queries being used on a website as it is resized. To do this it pulls out the list of available media queries by introspecting the site’s stylesheets.

So that’s the motivation. Let’s now explore a really basic example. Of course you can also dive into the pen’s source tabs to understand how that works.

Creating a new stylesheet

Let’s first create a barebones HTML document with no stylesheets and nothing other than the default user agent styling applied.

Our HTML document with no styles attached

Our HTML document with no styles attached

Creating a new stylesheet is as simple as creating an empty style element as a child of the head.

Creating our style element in the head with a bit of JavaScript

Creating our style element in the head with a bit of JavaScript

Adding rules

Once the style element is added the document now has a stylesheet. You can reference it via the document’s stylesheets property which returns an array of CSSStyleSheet instances. In our case we have a single instance to which we’ll add our rules.

CSSStyleSheet has a couple of useful methods. deleteRule removes an existing rule but the one we care about is insertRule. This takes a CSS rule to insert into the stylesheet and the index into the collection of existing rules where we want it inserted:

Our rule inserted and applied

Our rule inserted and applied

The cssRules property on CSSStyleSheet is an “array-like” ordered collection of type CSSRuleList. That means that it doesn’t support all of the methods that Array supports; but you can use subscript indexing and it has a length property. Here we’re using that length to provide the insertion point for our rule.

Finding CSS rules

In the example below we’ve created a function to loop over this collection looking for an existing rule. Each rule in the collection is an instance of CSSStyleRule and we use its selectorText property to match against the CSS selector for the rule we’re looking for.

Note that there are other types of CSSRuless than CSSStyleRule (for instance CSSMediaRule). But in our case since we control what’s added we can safely assume they don’t exist in this example.

Creating and using a function that finds an existing CSS rule in a stylesheet

Creating and using a function that finds an existing CSS rule in a stylesheet

Modifying rules

Once you have access to a CSSRule object it’s fairly trivial to set the style properties that are applied when the rule’s selector is matched.

CSSStyleRule has a style property that references the style declaration (of type CSSStyleDeclaration) for the rule. You can call setProperty on that object passing it a style name and value. There’s an optional final boolean parameter to setProperty that controls the !important attribute for the style.

Altering style properties for a rule and adding new ones

Altering style properties for a rule and adding new ones

Working with external stylesheets

If a document includes external stylesheets you can also access their CSS Rules in the same manner. To check if the stylesheet was externally loaded rather than defined in the document check whether its href attribute is set. It will be null for local stylesheets.

// get the list as an array for easy iteration
var sheets = [].slice.call(document.styleSheets);
sheets.forEach(function(sheet) {
  console.log("Sheet is %s", (sheet.href == null) ? "local" : "external");
});

Cross-domain restrictions

Note however that if a stylesheet was loaded from a different domain to the document you’ll be able to access its sheet object but you won’t be able to get to its rules list.

The cssRules property will be null for these stylesheets. This is because of cross-domain security restrictions. If you don’t control the stylesheet in question there’s not a lot you can do to get around this, short of proxying the stylesheet through your own server so that it comes from the same domain.

A tool to make life easier

If you’re looking for something to make this easier css.js is a neat little library that should do the trick. It allows you to create stylesheets and add, modify and remove rules easily. For example:

var sheet = cssjs.newSheet();
sheet
  .selector('div.container', {
    'width': '200px'
  })
  .selector('a', {
    'text-decoration': 'underline'
  });

// modify an existing rule
var anchorRule = sheet.selector("a");
anchorRule.properties({ "text-decoration" : "none" });