Instead of scripting className
, to change the appearance of several elements we can also script a rule that targets them.
Rather than swapping className
, we could just as well change the rule for the blue class in eight.css.
JavaScript represents a style sheet like eight.css with a CSSStyleSheet object. Those implement the members listed in two DOM interfaces, StyleSheet and CSSStyleSheet.
The following seven members come from StyleSheet:
disabled
href
media
ownerNode
parentStyleSheet
title
type
Additionally, four CSS-only members come from the CSSStyleSheet interface.
cssRules
ownerRule
deleteRule()
insertRule()
Internet Explorer does not implement any members from CSSStyleSheet. But there are Internet Explorer–only members which are as follows:
rules
imports
addRule()
addImport()
removeRule()
removeImport()
cssText
Before we can query a rule, we need to get at the style sheet that contains the rule. One way is to query the sheet member of a <link>
or <style>
element in Firefox, Safari, and Opera. Internet Explorer deviates from DOM, and implements a proprietary styleSheet member instead. With this in mind, our first steps will be the following:
var myStyleSheet = document.getElementsByTagName("link")[0]; myStyleSheet = myStyleSheet.sheet || myStyleSheet.styleSheet;
Another way to query a style sheet is by way of document.styleSheets, which contains an array-like object with one member for every <style>
or <link>
element having a rel attribute value set to "stylesheet". No browser adds imported style sheets to document.styleSheets
.
we could rewrite the previous sample like so:
var myStyleSheet = document.styleSheets[0];
Now myStyleSheet contains an object representing eight.css. Let’s find the rule for the blue class. It’s in a cssRules member for Firefox, Safari, and Opera, but in a rules member for Explorer. Those are both CSSRuleList objects. Those are array-like objects, which is to say their members are elements.cssRules.length or rules.length contains the number of members.
cssRules
contains both styling rules and @
directives like @import
. On the other hand, rules contains only styling rules. In other words, it contains the ones comprised of a selector and declaration block. So if a style sheet contains @
directives, cssRules.length will be greater than rules.length. In Internet Explorer, @import
directives are nowhere to be found, but any @page
directives are in an array-like object named pages.
@import
directives are missing in Internet Explorer, scripting imported style sheets remains doable by way of imports, addImport()
, and removeImport()
. Note that Internet Explorer splits grouped selectors into more than one rule. Therefore, for the following rule, Internet Explorer would add two members to rules, while Firefox, Safari, and Opera would add one member to cssRules:
div#mast form, div#mast h1 { display:inline; } In other words, Internet Explorer separates the previous rule into two like so: div#mast form { display:inline; } div#mast h1 { display:inline; }
The rules are numerically indexed in source code order. To find the rule with the selector "ul.blue a" in order to change the sprite to fuchsia; do a for loop like the following example.
var myStyleSheet = document.getElementsByTagName("link")[0]; myStyleSheet = myStyleSheet.sheet || myStyleSheet.styleSheet; var myRules = myStyleSheet.cssRules || myStyleSheet.rules; for (var i = myRules.length - 1; i > -1; i--) { if (myRules[i].selectorText && myRules[i].selectorText.toLowerCase() === "ul.blue a") { myRules[i].style.backgroundImage = "url(images/fuchsia.gif)"; break; } }
Three things in the for loop bear explaining.
@import
and other @
rules do not define a selectorText
or style member Calling String.toLowerCase()
on undefined returns an error. So, we use the && operator to skip any rules that do not define selectorText.Lets code a helper function named findRule() that takes two parameters.
element | contain the <link> or <style> element |
selector | contain the selector for the rule we want to find. |
function findRule(element, selector) { }
Let’s modify the body of the 'for' loop from the previous sample. In case there are no matching CSSStyleRule object, return null following the for loop.
function findRule(element, selector) { var sheet = element.sheet || element.styleSheet; var rules = sheet.cssRules || sheet.rules; selector = selector.toLowerCase() for (var i = rules.length - 1; i > -1; i--) { if (rules[i].selectorText && rules[i].selectorText.toLowerCase() === selector) { return rules[i]; } } return null; }
Now let’s call findRule() and change style by CSSStyleRule.style contains an object with the members listed in the CSSStyleDeclaration and CSS2Properties interfaces.
var myRule = findRule(document.getElementsByTagName("link")[0], "ul.blue a"); if (myRule !== null) { myRule.style.backgroundImage = "url(images/fuchsia.gif)"; }
var myRule = findRule(document.getElementsByTagName("link")[0], "a#saucony"); if (myRule !== null) { myRule.style.cssText = "background:url(images/green.gif) -99px -135px; top:205px"; }
<link>
or <style>
. The latter is markup, while the former is CSS. In other words, a style sheet is the CSS code in the file included by a <link>
element or contained by a <style>
element. So, you cannot, for example, retrieve a style sheet by its id member, since it does not have one. However, you can and probably retrieve the corresponding <link>
or <style>
element by its id. Lets see an example by referring its id.
var myRule = findRule(document.getElementById("spriteStyles"), "a#saucony"); if (myRule !== null) { myRule.style.cssText = "background:url(images/green.gif) -99px -135px; top:205px"; }