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.
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 aCSSStyleRule
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 ofCSSStyleRule
objects. You could find other objects such asCSSMediaRule
or evenCSSSupportsRule
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 aSTYLE
node, too. parentStyleSheet
- If this stylesheet was imported from another one, then this is the parent.
rules
CSSRuleList
object. Seems to be identical ascssRules
.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.