by

Exploring the CSSOM: Making a CSS Object Analyzer

A while back I wrote about an amazing discovery I’d made in which I learned that stylesheets are part of the DOM. I mentioned some properties to play with for fun and profit, and then heartily went on my way. Today I’d like to explore the CSSOM with a little more discipline, and also explain how I made a small debugger using the Table API.

Some Corrections and Whatnots

I did get some more technical facts wrong when I wrote my previous post. While content of the message was accurate, getting the technical issues right is a big deal if you want to actually build something based on this information. Sorry.

document.styleSheets is an Object

Previously, I’d said that this was an array. It is not. A simple typeof document.styleSheets tells you that we’re dealing with an object. But remember: in JavaScript, arrays are objects! I was actually wrong twice! What I should have done was look at the constructor. Had I done this previously, I’d have seen that document.styleSheets is made with the StyleSheetList() function to produce an object, not an array.

The Constructor is a Function - New Frank wins!
The Constructor is a Function – New Frank wins!

There is an Object Model for CSS

Another, slightly more glaring mistake that I’d made was saying that CSS was part of the DOM (Document Object Model). I should have done some more research. There’s a whole specification for the CSS Object Model. We’re actually dealing with the CSSOM, not the DOM. Therefore, a more accurate thing to say is that the CSSOM is linked to the DOM.

Getting into the CSSOM

Firstly, let’s get a list of some of the objects we’re dealing with in the CSSOM:

StyleSheetList
A list of all the stylesheets. Its properties are just a collection of CSSStyleSheet objects.
CSSStyleSheet
A single stylesheet. Contains some properties which are objects, boolean values, and strings that give us information about the stylesheet.
CSSRuleList
A list of all the style rules. The properties are the CSSStyleRule objects
CSSStyleRule
an object which is a single style rule. Contains some properties that are objects, boolean values, and strings that give us information about the style rule.
mediaList
An object which defines the medium to which a CSSStyleSheet or a CSSStyleRule might apply

Let’s review the hierarchy. The outermost object is StyleSheetList. It contains a list of CSSStyleSheet objects. The CSSStyleSheet object contains CSSRuleList which contains CSSStyleRule objects.

Step one: grab the object

We’d start by accessing the document.styleSheets object. Easy enough. Just remember that it’s an object — not an array.

Step two: watch your loops

Accessing the document.styleSheets object is simple enough. Since this isn’t an array, that means we can’t .forEach. We’re going to have to use the looping syntax for objects (for (var item in items)).

CSSStyleSheet

This object has nine properties attached to it. If you’ve read my previous post, then some of this will be a review.

cssRules
CSSRuleList object. A list of all the style rules. Its properties are primarily a collection of CSSStyleRule objects. You could find other objects such as CSSMediaRule or even CSSSupportsRule
disabled
A boolean value for whether or not a particular stylesheet is disabled. You can set this value.
href
String value. A fully qualified URL for the stylesheet
media
A MediaList object. Media information for this stylesheet. Specifically, if this stylesheet is targeted towards a medium (print or screen) or a specific window size, you’ll find it here.
ownerNode
DOM object. HTML element where this stylesheet gets called. Most likely it’s a LINK node, but it could be a STYLE node, too.
parentStyleSheet
If this stylesheet was imported from another one, then this is the parent.
rules
CSSRuleList object. Seems to be identical as cssRules.
title
String value. The title attribute in the node of the stylesheet. This is a getter, not a setter.
type
String Value. Just what it sounds like, the type of styles.

Step three: loop more objects

We've accessed the document.styleSheets object. We've looped through the objects. We're now accessing a single cssStyleSheet object. If we want to grab rules or cssRules, we're going to have to do a second for (var item in items) loop.

CSSStyleRule

cssText
A string value which is the entire rule set — the selector and the properties
parentRule
Not entirely sure on this one. Specifications say it's a "context object" which makes it ironically difficult to explain.
parentStyleSheet
The CSSStyleSheet object which contains this rule. Open it up and you'll entire a world of recursion that would make Christopher Nolan jealous.
selectorText
A string which is the text of the selector.
style
A CSSStyleDeclaration object. Open it up and you'll see it's a collection of properties and their values.

The CSSStyleRule is where, if we were building a super robust analyzer, we'd list out the properties of each object. Having the style and selector as separate properties could be really useful — especially if you wanted to build a tool that determined which style rules were getting applied.

The CSSOM Debugger

I wrote a small gist over on github that will analyze the CSSOM for you. It doesn't require any JavaScript libraries, and it floats a hair under 200 lines of JavaScript. It uses some vanilla JavaScript, which includes the table API.

Using the Analyzer

I make a single object attached to the window (window.analyzer). Just add this JavaScript where it seems right (footer of the page, or your main JavaScript file).

You just need to run window.analyzer.init(document.styleSheets, 'someID').

If there isn't a div on the page with your id, it'll make one. The analyzer is relatively generic JavaScript, so it can produce a table for other nested objects if you want (e.g. document.scripts ).

Check out what a live example would like like over on JSFiddle.

The Big Idea

This makes a great way to get some quality information about how the browser is parsing your stylesheets. Understanding that you're dealing with an entire object model, which includes objects attached to other objects, is key to building tools to manipulate and analyze styles.