Change style sheet

Opera 7 doesn't support the styleSheets[] array.

On this page I try to change the background colour of a PRE, not by accessing the element directly but by changing the entire style sheet of the page. Unfortunately browser incompatibilities are so severe that this script isn't really usable in practice yet.

Please note the difference between traditional DHTML and this example script. While in DHTML you change the styles of one specific HTML element on the page (usually identified by an ID), this example changes the style sheet of the entire document.

See also the W3C DOM - CSS compatibility table.

Definitions

A page contains one or more style sheets which in turn contain one or more rules which contain the actual style declarations. These are the styles in this page:

<link rel="stylesheet" href="../quirksmode.css">
<style>
<!--
@import url("test.css");

p,h2,h3 {
	padding-right: 10px;
}

pre.test + * {
	margin-right: 20%;
}

pre.test {
	background-color: #ffffff;
}
-->
</style>

The purpose of our test script will be to change the white background colour of the pre.test to its normal value transparent.

Style sheet

All linked and embedded style sheets are available through the document.styleSheets array. quirksmode.css, the general style sheet for the entire site, is document.styleSheets[0]. The special style block I added to this page is document.styleSheets[1]. We're going to do our tests on this special block of styles.

cssRules[] and rules[]

A rule is one set of style declarations for one or more elements. There are two ways to access these rules. W3C insists on the cssRules array while Microsoft has decided on the rules array. Both arrays work with index numbers, the first rule is (css)Rules[0], the second one [1] etc.

Please note that Explorer 5 on Mac supports both arrays.

Workaround:

var theRules = new Array();
if (document.styleSheets[1].cssRules)
	theRules = document.styleSheets[1].cssRules
else if (document.styleSheets[1].rules)
	theRules = document.styleSheets[1].rules

Now theRules contains all style rules.

Number of rules

This is the style sheet:

@import url("test.css");

p,h2,h3 {
	padding-right: 10px;
}

pre.test + * {
	margin-right: 20%;
}

pre.test {
	background-color: #ffffff;
}

Now at first you'd say that the special test style sheet has four rules: @import, then p,h2,h3, then pre.test + * and finally pre.test. Unfortunately that is not the case. Here are the selectors of the rules according to your browser:


A fine mess.

  1. undefined refers to the imported style sheet, which indeed does not have a selector. Explorer doesn't see the imported style sheet, though.
  2. Then, according to Explorer and Mozilla, the p,h2,h3 rule is not one rule but three separate ones, while Safari sees it as a rule for P only. As far as I understand the spec both variations are incorrect behaviour.
    "The selector consists of everything up to (but not including) the first left curly brace ({)."
    No browser adheres to this part of the spec.
  3. The pre.test + * is not recognized by Explorer Windows since it doesn't support this selector. Fair enough. Explorer Mac's behaviour, however, is weird. It changes the selector to pre.test *, which has a completely different meaning. Serious, very serious.

  4. Finally the pre.test is reasonably well supported, except that Safari adds unnecessary selection syntax.

So to access the pre.test rule Safari needs cssRules[3], Explorer rules[4] and Mozilla cssRules[5]. Lovely, isn't it?

No keys

So we encounter serious problems when trying to access the rule by index number. What I'd rather do is access them by keys, where the selector is the key. Something like:

document.styleSheets[1].cssRules['PRE.test']

so that I can access the rule of the PRE regardless of where in the style sheet it is. It does not seem to have occurred to either W3C or the browser vendors that web developers have a need for such a way of accessing rules. All documentation is completely silent on this point.

This failure means that it's nearly impossible to access the rule you want.

Style declarations

Let's pretend we have accessed the desired rule. Now we change one of its style declarations. The general syntax for this is

rule.style.color = '#0000cc';

The W3C approved way is

rule.style.setProperty('color','#00cc00',null);

Since it is vastly simpler I prefer the style.color syntax above setProperty().

Example

Try changing the background colour of the PRE's below.

Test PRE

The script

To avoid the rules problems noted above I use the fact that the pre.test rule is the last rule in the stylesheet. It's an ugly kludge, but it's the only way to get a proper cross-browser test case.

function changeIt()
{
	if (!document.styleSheets) return;
	var theRules = new Array();
	if (document.styleSheets[1].cssRules)
		theRules = document.styleSheets[1].cssRules
	else if (document.styleSheets[1].rules)
		theRules = document.styleSheets[1].rules
	else return;
	theRules[theRules.length-1].style.backgroundColor = 'transparent';
}