Cross-Browser DHTML TechNote:
Code Generator for Setting CSS1 Properties from JavaScript
Eric Krock, Netscape

Vladimir Ermakov, Netscape
and
Marcell Ortutay,
Plugged In Enterprises
Cascading Style Sheets, level 1 (CSS1), is a W3C Recommendation supported by Navigator 4.x, Internet Explorer and Gecko layout engine implemented in Netscape 6 and Mozilla browsers, which enables you to control the style (the format, or look and feel) of HTML elements using CSS1 markup. Because CSS1 rules are static and hard-coded, conditional style rules must be defined from JavaScript. This TechNote explains how to define CSS1 rules from JavaScript in a way that will work on Navigator 4.x, Netscape 6, Mozilla and Internet Explorer. It also provides a tool that automatically generates JavaScript code that defines CSS1 rules and runs on all three browsers.

CONTENTS
WHAT YOU NEED TO KNOW

Before reading this TechNote, you need to understand the syntax of CSS1 rules. In particular, you need to know how to select elements (with selectors like "P" for element names, "#someid" for ID attribute values, ".someclass" for CLASS attribute values, and ".someclass P #someid" for contextual selection), what the CSS1 property names are (such as "font-size" and "color"), and what the CSS1 property values are (such as "14pt" and "red").

If you need a basic introduction to CSS1, click through Netscape's Visual Tutorial on CSS1 on the Dynamic HTML Presentations Page. Tutorials available elsewhere on the web are listed on Netscape's Dynamic HTML Additional Resources Page. For full details, see the CSS1 Recommendation on the W3C Technical Reports Page.

If you don't know how to detect whether the user is using Navigator 4.x, Netscape 6 or Internet Explorer, read the Ultimate JavaScript Client Sniffer to learn how.

To learn how to access CSS1 properties from JavaScript in Navigator 4.x, click through Netscape's Visual Tutorial on Accessing CSS Properties from JavaScript via the Document Object Model on the Dynamic HTML Presentations Page.


WHY SET CSS1 PROPERTIES FROM JAVASCRIPT?

Netscape enthusiastically supports CSS1 and recommends that you use CSS1 markup whenever possible to define the style of your web page. It is easy to maintain and works across Navigator 4.x, Netscape 6, Mozilla and Internet Explorer. (Note: Since Navigator 4.x and Internet Explorer have not implemented every CSS1 property and since there are some differences in implementation and interpretation of the standard, use a third-party reference like Web Review's CSS1 Master Grid to confirm that a given property works on both browsers and all your target platforms. Gecko layout engine implemented in Netscape 6 and Mozilla supports all of CSS1 properties.)

However, CSS1 markup has one fundamental limitation. It is completely static and hard-coded. You cannot conditionally set element properties using CSS1. For example, you can't check the current screen size and then adjust element font sizes up or down to match the screen size. To do that, you must define CSS1 rules from JavaScript.

Remember this rule of thumb: "What you can do with CSS1, do with CSS1. What you can't do with CSS1, do with JavaScript." In other words, if the value is static and never need change, set it with CSS1 markup. If the value is conditional, set it with JavaScript.


HOW TO SET CSS1 PROPERTIES FROM JAVASCRIPT ACROSS BROWSERS

Navigator 4.x, Netscape 6, Mozilla and Internet Explorer make CSS1 properties of elements accessible from JavaScript through their Document Object Model (DOM). However, the Navigator 4.x DOM  and Internet Explorer DOM are different. They both implement parts of the W3C CSS1 standards but they cover different areas, so JavaScript code that defines CSS1 rules on one browser won't work on the other browsers. Gecko layout engine covers all of the properties in W3C CSS1 standards.

To define CSS1 rules from JavaScript and have them work compatibly across Navigator 4.x, Netscape 6, Mozilla and Internet Explorer, you must do four things:

  1. Insert into the document's HEAD an empty STYLE element with a unique ID; this becomes the target style sheet to which the rules are added on Netscape 6, Mozilla and Internet Explorer 4.0.
  2. Place the JavaScript for defining CSS1 rules in a SCRIPT element in the document's HEAD so it is evaluated before the BODY is parsed; if you define a CSS1 rule from JavaScript after the page has been loaded, in Navigator 4.x it won't be reflected until you reload the page.
  3. Determine which browser the user has, using JavaScript browser-detect code, like that in Ultimate JavaScript Client Sniffer.
  4. Map the CSS1 rule to Navigator 4.x JavaScript and Internet Explorer JavaScript, include both the Navigator 4.x code and the Internet Explorer code in your script, and conditionally evaluate the correct code for the user's browser.
For example, here is a code sample that sets the font size of P elements to 25 points on Navigator 4.x, Netscape 6, Mozilla and Internet Explorer. On Navigator 4.x, the command document.tags.P.fontSize="25pt"; is evaluated. On Internet Explorer , the command document.styleSheets["tssxyz"].addRule ("P", "fontSize:25pt"); is evaluated. On user agents implementing Gecko, the command document.getElementById('tssxyz').sheet.insertRule('P { fontSize: 25pt }', document.getElementById('tssxyz').sheet.cssRules.length ) is evaluated.
 
<STYLE ID="tssxyz" TYPE="text/css"></STYLE>
<SCRIPT LANGUAGE="JavaScript1.2"><!--
var agt=navigator.userAgent.toLowerCase();
if ( (parseInt(navigator.appVersion)==4) && 
     (agt.indexOf('mozilla')!=-1) && (agt.indexOf('spoofer')==-1)
               && (agt.indexOf('compatible') == -1) ) {
document.tags.P.fontSize="25pt";
}
else if (agt.indexOf('gecko') != -1) { 
document.getElementById('tssxyz').sheet.insertRule('P { fontSize: 25pt }', document.getElementById('tssxyz').sheet.cssRules.length )
}
else if ( (parseInt(navigator.appVersion)>=4) && 
     (agt.indexOf('msie') != -1) ) {
document.styleSheets["tssxyz"].addRule ("P", "fontSize:25pt");
}
//--></SCRIPT>

To map CSS1 rules to Navigator 4.x, Netscape 6, Mozilla and  Internet Explorer JavaScript, you must understand the structure of CSS1 rules. CSS1 rules have three parts: a selector which chooses one or more elements on the page based on their tag name, ID attribute value, CLASS attribute value, CLASS and name, or context; a property name like "font-size"; and a value for the property, such as "24pt." Here is the structure of a CSS1 rule with examples of the five kinds of selectors and a property name and value:
 

Part of Rule (kind of selector) Example
selector tag name: "P"
class: ".warning"
ID: "#foo"
class and tag name: "P.warning"
contextual: "P B.warning #foo"
property name "font-size"
value "24pt"

To detect which browser the user is using, use the JavaScript browser-detect code in The Ultimate JavaScript Client Sniffer.


SETTING CSS1 PROPERTIES FROM JAVASCRIPT ON NAVIGATOR 4.x

To define CSS1 rules from JavaScript in Navigator 4.x, there are two steps:

  1. Map the CSS1 selector to its JavaScript equivalent.
  2. Map the CSS1 property name to its JavaScript equivalent.
Mapping CSS1 Selectors to Navigator 4.x JavaScript Selectors

CSS1 selectors are mapped to Navigator 4.x JavaScript selectors as follows:

  1. Tag name selectors are mapped to the document.tags object, followed by the tag name.
  2. CLASS attribute selectors are mapped to the document.classes object, followed by the class name and "all."
  3. ID attribute selectors are mapped to the document.ids object, followed by the ID attribute value.
  4. Combined class/name selectors are mapped to the document.classes object, followed by the class name and the tag name.
  5. Contextual selectors are mapped to the document.contextual method, followed in parentheses by a comma-separated list of the individual selectors mapped one at a time.
This process can be understood easily by examining the following table of examples:
 
Mapping CSS1 Selectors to Navigator 4.x JavaScript Selectors
Type of selector CSS1 example JavaScript equivalent
tag name "P" document.tags.P
class ".warning" document.classes.warning.all
identifier "#foo" document.ids.foo
tag name and class "P.warning" document.classes.warning.P
contextual "P B.warning #foo" document.contextual (document.tags.P,document.classes.warning.B, document.ids.foo)

Mapping CSS1 Property Names to JavaScript Property Names

Mapping CSS1 property names to JavaScript property names is easy. You drop the hyphens and change each character after a hyphen to uppercase. For example, the CSS1 property name "font-size" becomes the JavaScript property name "fontSize".

The only exception is properties that take multiple values. These are mapped to methods which have a trailing "s" after their names. For example, border-width becomes borderWidths(), margin becomes margins(), and padding becomes paddings().

Example of Mapping CSS1 Rule to Navigator 4.x JavaScript

As an example, this CSS1 rule:
 

P { font-size: 25pt }

...becomes this JavaScript command in Navigator 4.x:
 

document.tags.P.fontSize="25pt";



SETTING CSS1 PROPERTIES FROM JAVASCRIPT ON USER AGENTS IMPLEMENTING GECKO LAYOUT ENGINE

To define CSS1 rules from JavaScript in Netscape 6 and Mozilla, there are two steps:

  1. In the HEAD, add an empty style sheet with a unique ID; this becomes the target style sheet to which we add new CSS1 rules with the Gecko insertRule() method.
  2. Within the JavaScript script, use the Gecko insertRule() method to add CSS1 rules to the target style sheet:
By default, the tool below uses this markup as the target STYLE element in Netscape 6, Mozilla and Internet Explorer:
 
<STYLE ID="tssxyz" TYPE="text/css"></STYLE>

The ID "tssxyz" stands for "Target Style Sheet," with "xyz" added to increase the "uniqueness" of the identifier and reduce the likelihood that it would collide with an ID anyone else has used already.

The Gecko insertRule method has this syntax:
 

document.getElementByID(targetSheetID).sheet.insertRule
(selector + "{" propName + ":" + value + "}", locationOfRule);

In order to add the CSS1 rule to the end of the target style sheet, set the location of the rule in the style sheet to:
 

targetSheetID.cssRules.length

For example, this CSS1 rule:
 

H1 { color: red }

...becomes this JavaScript command in Netscape 6 and Mozilla:
 

document.getElementById("tssxyz").sheet.insertRule("H1 { color: red }", tssxyz.cssRules.length);



SETTING CSS1 PROPERTIES FROM JAVASCRIPT ON INTERNET EXPLORER 4.0 AND ABOVE

To define CSS1 rules from JavaScript in Internet Explorer, there are two steps:

  1. In the HEAD, add an empty style sheet with a unique ID; this becomes the target style sheet to which we add new CSS1 rules with the Internet Explorer  addRule() method.
  2. Within the JavaScript script, use the Internet Explorer addRule() method to add CSS1 rules to the target style sheet:
The tool below uses this markup as the target STYLE element in Internet Explorer, Mozilla and Netscape 6:
 
<STYLE ID="tssxyz" TYPE="text/css"></STYLE>

The ID "tssxyz" stands for "Target Style Sheet," with "xyz" added to increase the "uniqueness" of the identifier and reduce the likelihood that it would collide with an ID anyone else has used already.

The Internet Explorer addRule method has this syntax:
 

document.styleSheets[targetSheetID].addRule (selector, propName + ":" + value);

As an example, this CSS1 rule:
 

P { font-size: 25pt }

...becomes this JavaScript command in Internet Explorer:
 

document.styleSheets["tssxyz"].addRule ("P", "fontSize:25pt")


ENSURING EQUAL RULE PRIORITIES ACROSS BROWSERS

To achieve the same result, the rules must be added by JavaScript at the same place in the HTML markup in the same order. This will make sure they get the same CSS1 priority level in Internet Explorer, Navigator 4.x, Mozilla and Netscape 6. (Later rules have higher priority.)

Obeying these three rules will ensure that rules are added at the same position in the document in the same order across both browsers:

For example, using the following markup in the HEAD will ensure that the sample CSS1 rule, which sets the background color of the hint class elements to yellow, is given equal priority in Navigator 4.x, Netscape 6, Mozilla and Internet Explorer:
 
<STYLE ID="tssxyz" TYPE="text/css"></STYLE>
<SCRIPT LANGUAGE="JavaScript1.2">
var agt=navigator.userAgent.toLowerCase();
if ( (parseInt(navigator.appVersion)==4) && 
     (agt.indexOf('mozilla')!=-1) && (agt.indexOf('spoofer')==-1)
               && (agt.indexOf('compatible') == -1) ) {
document.classes.hint.all.backgroundColor="yellow";
}
else if (agt.indexOf('gecko') != -1) { 
document.getElementById('tssxyz').sheet.insertRule('.hint { background-color: yellow }', document.getElementById('tssxyz').sheet.cssRules.length )
}
else if ( (parseInt(navigator.appVersion)>=4) && 
     (agt.indexOf('msie') != -1) ) {
document.styleSheets["tssxyz"].addRule (".hint", "background-color:yellow");
}
</SCRIPT>


A HINT ABOUT BACKGROUND COLOR

One CSS1 property to pay special attention to for cross-browser compatibility is backgroundColor, because Navigator 4.x hasa significant difference in its implementation of this property then Internet Explorer, Netscape 6 and Mozilla. Navigator 4.x displays an element's background color only behind the element's text. Internet Explorer, Netscape 6 and Mozilla display the background color all the way to the right edge of the screen.

For example, this markup:
 

<P STYLE="background-color: red">red background></P>

... looks like this in Navigator 4.x:

red background of P looks like this in Navigator 4.x

...and looks like this in Internet Explorer, Netscape 6 and Mozilla:
 
red background of P looks like this inInternet Explorer, Netscape 6 and Mozilla

 To make the background color behind only the element's text in all three browsers, set background color on a <SPAN> enclosing the text. For example, this markup:
 

<P><SPAN STYLE="color:white; background-color:black">
white on black behind text in all three browsers
</SPAN></P>

...will produce this result in all browsers:

white on black behind text only in both browsers

To make the background color to the right edge of the page in all three browsers, use 1x1 TABLE element with WIDTH=100%. For example, this markup:
 

<TABLE WIDTH=100%>
<TR><TD STYLE="color:white; background-color:black">
<SPAN STYLE="color:white; background-color:black">
white on black to right edge of page in all three browsers
</SPAN>
</TD></TR>
</TABLE>

...will produce this result in all browsers:
white on black to right edge of page in all browsers

For a positioned HTML element that covers a rectangular area, to make the background color behind the entire element area (regardless of the amount of text), use a 1x1 borderless table with WIDTH and HEIGHT and background properties set to match element. For example, this markup:
 

<STYLE TYPE="text/css">
#samplerect { position: relative }
</STYLE>

<DIV ID="samplerect" STYLE="width: 500px; height: 60px; background-color: red">
<TABLE WIDTH=500 HEIGHT=60 STYLE="background-color: red">
<TR><TD>some text that doesn't fill the element</TD></TR>
</TABLE>
</DIV>

...will produce this result in all browsers:

some text that doesn't fill the element

AUTOMATING THE MAPPING ACROSS BROWSERS

The free tool in this TechNote automatically generates JavaScript code that defines CSS1 rules and works on Navigator 4.x, Netscape 6, Mozilla and Internet Explorer. All you have to do is type the CSS1 selector, property name, and value (which together define a CSS1 rule) into the form and click "Generate Code." The form will automatically convert the CSS1 rule into HTML and JavaScript which works on all three browsers. You can then copy and paste the generated STYLE and SCRIPT elements into the HEAD of your HTML page, and the elements' CSS1 properties will be set from JavaScript on both browsers. (Make sure to copy and paste all of the markup, including the empty STYLE element which is generated to make the code work on Netscape 6, Mozilla and Internet Explorer! )

Usually, you will use this tool in one of two ways: setting a CSS1 property to the value of a variable, or conditionally evaluating JavaScript style code. (These are both things which cannot be done using static CSS1 markup.) You can also create JavaScript commands which set CSS1 properties to fixed, unchanging, hard-coded values, but in that case, you might as well use CSS1 markup instead of JavaScript.

Setting a CSS1 Property to the Value of a Variable

The easiest way to use this tool is to define a JavaScript variable that holds the value you want to use for some CSS1 property. The JavaScript code that determines the value can be as sophisticated as you wish. Usually, it will be a series of if statements.

For example, to create a paragraph P element that uses the same amount of screen space regardless of the current display size, you could determine the font size of the P element in points (pt) as follows:

Translated into JavaScript, that is:
 
if (screen.width < 700) pFontSize="22pt";
else if (screen.width < 900) pFontSize="28pt";
else pFontSize="36pt";

This "initialization code" stores the font size you want to use for the P element in the variable pFontSize. You would paste this code into the "Initial JavaScript code" text area on this form. (We've done it for you in the sample below.)

Now you just need to create a rule that sets the font size for P elements to the JavaScript variable pFontSize. To do that, you create a rule using the form below. In a single row, you define the selector, property name, and value for that rule.

To create the rule "Set the font size for all P elements to the variable pFontSize," you would enter:

We've done those steps for you as well in the sample rules below. To create the JavaScript and HTML that will implement these rules on both browsers, all you have to do is click the "Generate Code" button. To view a sample document with test elements that use the rules, you click the "View Sample HTML Page" button.

Conditionally Evaluating JavaScript Style Code

Sometimes you will want to check the value of a variable in an if or switch statement and then, depending on the value of the variable, evaluate or ignore one or more blocks of JavaScript style definitions. This tool won't generate the if statements for you, but you can generate all the rules and then fill in the if statements yourself.

For example, suppose that you were displaying a weekly calendar and wanted today's day name to be displayed in red text with all the other days of the week displayed in the default color black. You could fill in the form's "Initial JavaScript code" field with code to retrieve the current day:
 

var theDate = new Date(); 
var today=theDate.getDay();

Then you would fill in the table with rules to set every day's color to red:
 

CSS1
Selector:
CSS1
Property Name:
CSS1
Value:
.sundayColor color red
.mondayColor color red
.tuesdayColor color red
.wednesdayColor color red
.thursdayColor color red
.fridayColor color red
.saturdayColor color red

...and click "Generate Code" to generate the JavaScript code for all the rules on both browsers automatically. Then you would edit the code and add if statements to ensure that only the rule for setting the color of today's day name is evaluated. The finished code would look like this:
 

 <STYLE ID="tssxyz" TYPE="text/css"></STYLE>
  <SCRIPT LANGUAGE="JavaScript1.2">
<!-- 
var theDate = new Date();
var today=theDate.getDay();

 var agt=navigator.userAgent.toLowerCase();
if ( (parseInt(navigator.appVersion)==4) && 
     (agt.indexOf('mozilla')!=-1) && (agt.indexOf('spoofer')==-1) 
               && (agt.indexOf('compatible') == -1) ) 
{   
 if (today==0)
 document.classes.sundayColor.all.color="red";
     if (today==1) document.classes.mondayColor.all.color="red";
     if (today==2) document.classes.tuesdayColor.all.color="red"; 
     if (today==3) document.classes.wednesdayColor.all.color="red"; 
     if (today==4) document.classes.thursdayColor.all.color="red"; 
     if (today==5) document.classes.fridayColor.all.color="red"; 
     if (today==6) document.classes.saturdayColor.all.color="red";  
} 
else if (agt.indexOf('gecko') != -1)
{
     if (today==0) document.getElementById('tssxyz').sheet.insertRule('.sundayColor { color: red }', document.getElementById('tssxyz').sheet.cssRules.length );
     if (today==1) document.getElementById('tssxyz').sheet.insertRule('.mondayColor { color: red }', document.getElementById('tssxyz').sheet.cssRules.length );
     if (today==2) document.getElementById('tssxyz').sheet.insertRule('.tuesdayColor { color: red }', document.getElementById('tssxyz').sheet.cssRules.length );
     if (today==3) document.getElementById('tssxyz').sheet.insertRule('.wednesdayColor { color: red }', document.getElementById('tssxyz').sheet.cssRules.length );
     if (today==4) document.getElementById('tssxyz').sheet.insertRule('.thursdayColor { color: red }', document.getElementById('tssxyz').sheet.cssRules.length );
     if (today==5) document.getElementById('tssxyz').sheet.insertRule('.fridayColor { color: red }', document.getElementById('tssxyz').sheet.cssRules.length );
     if (today==6) document.getElementById('tssxyz').sheet.insertRule('.saturdayColor { color: red }', document.getElementById('tssxyz').sheet.cssRules.length );
} 
else if ( (parseInt(navigator.appVersion)>=4) &&       (agt.indexOf('msie') != -1) ) 
{    if (today==0) document.styleSheets["tssxyz"].addRule (".sundayColor", "color:red");
     if (today==1) document.styleSheets["tssxyz"].addRule (".mondayColor", "color:red");
     if (today==2) document.styleSheets["tssxyz"].addRule (".tuesdayColor", "color:red");
     if (today==3) document.styleSheets["tssxyz"].addRule (".wednesdayColor", "color:red");
     if (today==4) document.styleSheets["tssxyz"].addRule (".thursdayColor", "color:red"); 
     if (today==5) document.styleSheets["tssxyz"].addRule (".fridayColor", "color:red");
     if (today==6) document.styleSheets["tssxyz"].addRule (".saturdayColor", "color:red");
 } //-->


TESTING THE MAPPING

This test page demonstrates that JavaScript generated by this code generator works compatibly across both browsers for many CSS1 properties and values.


KNOWN BUGS

 Netscape 4.74 crashes when there is an empty style sheet in the <HEAD>. If you are using this browser I would strongly suggest you to upgrade it to 4.76 or Netscape 6. You can find both of these browsers here.


CROSS-BROWSER DHTML CODE GENERATOR FOR SETTING CSS1 PROPERTIES FROM JAVASCRIPT

ID of STYLE element to use for Internet Explorer, Mozilla and Netscape 6: 

Initial JavaScript code: (variable initialization, etc.)

Type in the CSS1 Selector/Property Name/Value triplets you wish to set from JavaScript. They will automatically be converted to JavaScript which sets the CSS1 rules and works on Navigator 4.x, Mozilla, Netscape 6 and Internet Explorer 4+.

Usage note: Only enter a single value into the Value field. Although some CSS1 properties like margin, border-width, and padding-width can take 1-4 values, this code generator does not support setting one CSS1 property to multiple values. For example, setting the border width on each side to a different value by typing in the property name border-width and the four values 10px 5px 3px 1px will not work. Instead, set the CSS1 property to a single value (for example, by typing in the property name border-width and the single value 10px) or use other property names to set the values one at a time (for example, by using four rows to enter the four property-value pairs of border-top-width and 10px, border-right-width and 5px, border-bottom-width and 3px, border-left-width and 1px).

CSS1
Selector:
CSS1
Property Name:
CSS1
Value:
Value is
variable?


DOWNLOADING THIS TECHNOTE

If you would like to download this TechNote (including the code generator) to your local machine, just download this WinZIP-compatible ZIP file with all of the necessary files.

For the latest technical information on Sun-Netscape Alliance products, go to: http://developer.iplanet.com

For more Internet development resources, try Netscape TechSearch.


Copyright © 1999 Netscape Communications Corporation.
This site powered by: Netscape Enterprise Server and Netscape Compass Server.