Notes Information Apocalypse

Style Object Stew

Think you understand the basics of HTML and the Document Object Model?

Start with a basic CSS selector:

#items {
  width:180px;
  height:220px;
}

Now the HTML that this CSS decorates:

An item

Another item

We know that we can return an object reference to the element using Javascript and the DOM:

var items = document.getElementById('items');

So it must be pretty easy to get the height of the element, right?

An intuitive assumption would be to examine the items.style.height property. But when this script executes, items.style.height is undefined. WTF is going on??

The answer it seems, is that the style object does not exactly represent the all the styles declared for the element. To be accessible as part of the style object by default, the properties must be declared inline.

An item

Another item

This is probably not what we want. It imposes presentation specific rules on the flow of the HTML, and isn't flexible to edit or reuse across multiple templates or documents. Luckily it's not the only way to access declared styles in javascript, but now the solution will require browser specific code.

Browser specific code? Aren't we just harking back to the bad old days of awkward and incompatible DHTML mess?

Maybe. Nowdays, there's really only a single arbiter in this situation - the W3 DOM. Browsers either support it or they don't. If they don't, they are probably IE6 Win, which is likely to be used by more than 80% of your audience, so in most cases, you can get away with allowing for just two conditions - standards compatible and IE compatible.

The good news is that in many cases, you don't actually need to access the declared value of a CSS property, as the browser provides a set of calculated offset properties, which should be available to all document elements by default. In this case, Items.offsetHeight will return 220px in all major browsers, but bear in mind that this is not always going to work. Eg:

#items {
position:absolute;
left:0;
top:0;
height:220px;
display:none;
}

Here, the element is set to display: none and because it is not rendered by the browser, the declared height will not be calculated as an offsetHeight. So we still need a way to access the declared styles of an element - browser specific properties are unavoidable.

Dealing with IE6 is relatively straightforward, as it stashes all declared CSS rules for an element in the proprietry currentStyle reference. So without resorting to inline CSS, you could access the declared height of the element as above, using items.currentStyle.height and it would happily return 220px. Mozilla lacks such a handy shortcut, so the solution here requires the use of the DOM API. Specifically, it requires using the getComputedStyle method of the document.defaultView object.

Here is a simple example of how to use this in a script, by attaching a new property Item.height, that can be used as cross browser reference to the declared height value:

var items = document.getElementById('items');
if (!document.all) {
// Mozilla compatible
cStyle = document.defaultView.getComputedStyle(Items, '');
items.height = cStyle.getPropertyValue("height");
} else {
// IE compatible
items.height = items.currentStyle.height;
}

Using the CSS declared above, items.height would be set to 220px in both IE and Mozilla.

What about Safari you ask? What about Safari? Ha. Next question...

Note that currentStyle and getComputedStyle are not entirely the same. It appears that currentStyle will return the value from the cascade, wheras getComputedStyle always returns an exact unit. This shouldn't be a problem in most instances, and it would probably be the least of your worries if you are in the situation of dealing with overrides from multiple CSS rules anyway.