Home & blog  /  2011  /  Dec  /  view post  /

Being Firebug, pt1: reading CSS in Javascript

posted: 03 Dec '11 20:16 tags: css, stylesheet, Firebug, Dragonfly

Ever wondered how Firebug, Dragonfly and other debug tools manage to detect which styles apply to an element, whether they're currently active, what stylesheet (if any) they're coming from, and other such info?

In a two-part post, I'll be looking at how that's done in Javascript. Yes, it's all Javascript.

I find many junior-to-intermediate JS developers are surprised to find out JS has a rich API for this sort of thing (given Javascript's limited API generally).

In this first post I'll be looking at how you read the styles loaded into your page. In the second post, in the coming week, I'll look at how you can then work out which styles apply to a particular element - and whether they're currently active or have been overridden by another style.

The API

There's two key parts here: one to detect rules and styles from loaded stylesheets (or inline <style> tags), and another to detect current styles - i.e. the current styles active on an element, from whatever source.

The latter are called computed styles - i.e. the current style, from whatever source.

Getting computed styles

As is often the case, this is a case of Microsoft vs. the rest. Non-IE browsers define a function, getComputedStyle, which accepts two arguments, but you'll nearly always pass only one (more on that later) - the first one - which should be a reference to an element. IE, however, defines an object on each element's prototype, currentStyle, which contains sub-properties for each current style.

Let's get the computed colour of the first paragraph in a page. As ever in a situation like this, where there's differing approaches for different browser, a spot of feature detection is in order:

1var para = document.getElementsByTagName('p')[0];

2var colour = para.currentStyle ? para.currentStyle.color : getComputedStyle(para, null).color;

That should be pretty obvious what's happening - if element.currentStyle is supported, we use that - else we use getComputedStyle().

Sidenote: I actually prefer IE's way of doing things here. I know, I know, it's not often you hear a developer say that. But it just seems so much more sensible that, since these properties are, by their nature, dynamic, and not unchangably set at runtime, they live ON the element. The non-IE way of defining a function instead seems at odds with Javascript's prototypal inheritance philosophy. After all, plenty of other dynamic properties live on the elements themselves, in all browsers, such as offsetHeight.

Getting CSS rules/styles

Javascript has an API for reading the rules and styles of every linked stylesheet or inline <style> tag in your page. It does this through the document.styleSheets object.

alert('There are '+document.styleSheets.length+' stylesheets or style tags in this page');

Each element of the object represents a linked stylesheet or document.styleSheets in your page. Go a level deeper, and you can access the rules and styles within. Again, however, there's differences in the name of this property between IE and the rest: cssRules in non-IE browsers, and rules in IE.

Let's get the total styles for the whole page, into a nice, tidy array in the following format:

1var css = [

2     {

3         href: 'some/stylesheet.css',

4         rules: {

5             '#some .thing a': {

6                 color: '#FF0000'

7             }

8         }

9     }

10]

Hopefully the structure is pretty obvious: an array containing a sub-object for each stylesheet/style tag, which in turn contains its href (or, if inline, simply 'inline') and a rules sub-object, named after the selector text of the rule, and which contains the styles/values themsleves. So let's get it on:

1//set up a log

2var css = [];

3    

4//iterate over stylesheets and style tags

5for (var u=0, numSheets = document.styleSheets.length; u<numSheets; u++) {

6    

7     //make a shortcut alias to this sheet or tag

8     var sheet = document.styleSheets[u];

9    

10     //log this sheet or tag's rules (depends on browser)

11     var rules = sheet.rules ? sheet.rules : sheet.cssRules;

12    

13     //make a sub-object for this sheet or style tag

14     var sheetObj = {};

15    

16     //log its href or, if inline, simply "inline"

17     sheetObj.href = sheet.href ? sheet.href : 'inline'

18    

19     //add a sub-object for its rules

20     sheetObj.rules = {};

21    

22     //iterate over the rules and log each in sub-object

23     for (var o=0, numRules = rules.length; o<numRules; o++) {

24    

25         //make a sub-object to log this rule's its styles

26         var ruleObj = {};

27    

28         //iterate over its styles and log each in a sub-object

29         for (var i=0, numStyles = rules[o].style.length; i<numStyles; i++) {

30    

31             //get the style's name, e.g. "color", and value

32             var styleName = rules[o].style[i],

33                 styleVal = rules[o].style[styleName];

34    

35             //log this style and its value in our sub-object

36             ruleObj[styleName] = styleVal;

37    

38         }

39    

40         //log this rule's object in the sheet rules object,

41         //using the rule's selector text as its name

42         sheetObj.rules[[rules[o].selectorText]] = ruleObj;

43    

44     }

45    

46     //lastly, log this sheet's object in our css array

47     css.push(sheetObj);

48    

49}

50    

51//log our findings in the console

52console.log(css);

Hopefully the comments in that code block make it quite clear what's going on. Essentially, it's a case of rather unprettily drilling down, through a series of nested for loops (urgh...) from the stylesheet to the rules, to the actual styles.

Summary

So there you have it. Note that you'll need to run the above only once the window (or at least DOM) has loaded, otherwise the browser won't be ready to talk to Javascript about stylesheets just yet.

Look out for the next part in this post, later in the coming week, in which we'll see not only how to detect what styles are present in a page, but which apply to a particular element and whether they're currently active, ala Firebug, Dragonfly and other debug tools.

Oh and be sure to bookmark the Javascript API references for the DOM StyleSheet and DOM CSS rule objects.

Enjoy.

post new comment

Comments (2)

Maximum, at 21/12/'11 07:49, said:
I always wondered how Firebug did that magic. Now thanks to you, the simplest way of knowing the style is revealed.

I'm a good JS developer, but never looked into these areas. i shall from here on..

thanks again dude!
Jeroen, at 1/06/'12 16:44, said:
This sheet rules! :-) Thanks a lot.