Приглашаем посетить
Блок (blok.lit-info.ru)

Global Style Sheets

Global Style Sheets

The previous two techniques involve changing the style of a single instance of an element at a time. By manipulating global style sheets, a script can change the style of many elements all at once. The global style sheet object model provides complete access to the global style sheets defined both within a document and in external files. Global style sheets contained within the page are associated with the document through the Style element; the Link element is used to associate an external style sheet file with the page. With the global style sheet object models any style sheet can be completely customized, style sheets can be turned on and off, rules within the style sheet can be accessed and changed, and new rules can be added to quickly change the style of the entire document.

Dynamically modifying the global style sheet is an extremely powerful operation, but it can also be costly. Every time a new rule is added or removed, or a style is changed in the global style sheet, the entire document is recalculated. Therefore, take care to minimize the number of operations you perform on the style sheet. When multiple changes to the document are necessary, an efficient technique is to define multiple style sheets and enable and disable them. This technique is introduced in the section "Providing a List of Alternative Style Sheets" later in this chapter.

The styleSheets Collection

The document exposes the set of style sheets associated with it through a styleSheets collection. The styleSheets collection contains all the global style sheets, whether they are contained in the document or linked from an external file. In the styleSheets collection, as in all other collections in Dynamic HTML, objects appear in the same order in which they appear in the document.

The styleSheets collection contains styleSheet objects, not element objects. There is a relationship between the styleSheet objects in the styleSheets collection and the style and link objects in the all collection. Each styleSheet object exposes an owningElement property that returns the style or link object that defined the style sheet. Each style and each Link element that associates a style exposes a styleSheet property that returns the styleSheet object.

Referencing a Style Sheet

All elements in the document support the ID attribute. The ID attribute in the Style and Link elements serves a dual purpose: it provides the index value to directly access the element through the all collection and it provides the index value to directly access the styleSheet object in the styleSheets collection. It is important to recognize that in the all collection, a particular ID attribute references an actual style or link object, while in the styleSheets collection, it references the associated styleSheet object. The following example shows how to reference a style object and its associated styleSheet object using an ID, and how to reference each of those objects from the other:

<HTML>
   <HEAD>
      <TITLE>styleSheet Object vs. Style Element</TITLE>
      <STYLE ID="demo" TYPE="text/css">
         BODY {color:red}
      </STYLE>
      <SCRIPT LANGUAGE="JavaScript">
         // Return the style object.
         var styleElement = document.all["demo"];
         // Return a styleSheet object.
         var styleSheetObject = document.styleSheets["demo"];

         // Access each of these objects from the other.
         // Both alert boxes display true.
         alert(styleSheetObject.owningElement == styleElement);
         alert(styleElement.styleSheet == styleSheetObject);
      </SCRIPT>
   </HEAD>
   <BODY>
      Contents
  </BODY>
</HTML>

Providing a List of Alternative Style Sheets

The styleSheets collection can be used to enumerate all the style sheets in the document. Each style sheet can be individually enabled or disabled, turning on or off the application of the style sheet to the document. This technique enables a page to expose multiple styles for the user to select from; it can also be used to provide multiple views of the data.

Providing alternative style sheets has several advantages over dynamically modifying a single style sheet through code. Updating and maintaining alternative style sheets is easier than updating and maintaining scripts that modify a single style sheet. Also, code to switch between alternative style sheets is more efficient than code to modify a style sheet, especially if the code has to change a large number of styles. When you switch style sheets, the document is recalculated and displayed twice, once when the current style sheet is disabled and a second time when the new style sheet is enabled. In contrast, when you modify a single style sheet, the document is recalculated after each style is changed.

The DISABLED Attribute

The Style and Link elements support the DISABLED attribute, which initially disables a style sheet. You can use this attribute to control which style sheets are initially applied to the document. Scripts can later reset the Style and Link elements' corresponding disabled properties to change which style sheets are applied to the document. The examples that follow use this technique.

Providing Multiple Views

The following Web page allows the user to switch between different views of the same data. This technique is useful for providing several levels of detail at which to view the underlying data without requiring multiple pages to be downloaded. This example requires the user to explicitly choose between views. Your code can also change the view in response to other factors—for example, the size of the browser—as shown in the "Adaptive Layout Techniques" section later in this chapter.

<HTML>
   <HEAD>
      <TITLE>Multiple Views</TITLE>
      <STYLE ID="all" TYPE="text/css">
         #headOnly {display:none}
         #allText {color:red; cursor:default}
      </STYLE>
      <STYLE ID="headers" TYPE="text/css" DISABLED>
         #allText {display:none}
         #headOnly {color:navy; cursor:default}
         DIV {display:none}
      </STYLE>
   </HEAD>
   <BODY>
      <H1> Demonstration of Multiple Views</H1>
      <P ID="allText" 
            ONCLICK="document.styleSheets[`headers'].disabled = false;
               document.styleSheets[`all'].disabled = true;">
         You are viewing an entirely expanded version of the
         document. Click on this paragraph to switch views.</P>
      <P ID="headOnly" 
            ONCLICK="document.styleSheets[`headers'].disabled = true;
               document.styleSheets[`all'].disabled = false;">
         You are viewing only the headers of the document. 
         Click on this paragraph to switch views.</P>
      <H2>Multiple Views</H2>
      <DIV>Using the CSS object model, you can provide multiple views
         of the data.
      </DIV>
      <H2>Swapping Data</H2>
      <DIV>You can also swap data displays. You can include
         predefined data in the document and selectively hide and
         display it.
      </DIV>
   </BODY>
</HTML>

Figure 11-1 demonstrates the two views of the document, with the two different style sheets applied. When the user clicks on the first paragraph, the style is automatically switched and different information is shown or hidden.

Global Style Sheets Figure 11-1. Two views of a document obtained by alternating between style sheets contained within the document.

Selecting from Multiple Style Sheets

In the preceding example, the user clicks on a paragraph to change the display option. The following code takes an alternative approach; it provides a drop-down list from which the user can select a display option:

<HTML>
   <HEAD>
      <TITLE>Listing Style Sheets</TITLE>
      <STYLE ID="all" TITLE="Entire Document" TYPE="text/css">
         #headOnly {display:none}
         #allText {color:red; cursor:default}
      </STYLE>
      <STYLE ID="headers" TITLE="Headers Only" TYPE="text/css"
            DISABLED>
         #allText {display:none}
         #headOnly {color:navy; cursor:default}
         DIV {display:none}
      </STYLE>
      <SCRIPT LANGUAGE="JavaScript">

         function selectSheet(s) {
            for (var intLoop = 0;
                  intLoop < document.styleSheets.length; intLoop++)
               document.styleSheets[intLoop].disabled =
                  (s.selectedIndex != intLoop);
         }
      </SCRIPT>
   </HEAD>
   <BODY>
      <H1>Listing Alternative Style Sheets</H1>
      <P>Select a View:
      <SELECT ONCHANGE="selectSheet(this);">
         <SCRIPT LANGUAGE="JavaScript">
            // Dynamically build list of options.
            for (var intLoop = 0;
                  intLoop < document.styleSheets.length;
                  intLoop++)
               document.write("<OPTION>" +
                  document.styleSheets[intLoop].title);
         </SCRIPT>
      </SELECT>
      <P ID="allText">
         You are viewing an entirely expanded version of the document.
      </P>
      <P ID="headOnly">
         You are viewing only the headers of the document.
      </P>
      <H2>Multiple Views</H2>
      <DIV>Using the CSS object model, you can provide multiple views
         of the data.
      </DIV>
      <H2>Swapping Data</H2>
      <DIV>You can also swap data displays. You can include
         predefined data in the document and selectively hide and
         display it.
   </BODY>
</HTML>

The drop-down list displays the TITLE attributes of the style sheets. TITLE attributes are available on all elements; they are used here to give the style sheets useful names. When the user selects an item from the list, the style sheet with the corresponding TITLE attribute is applied to the document.

Figure 11-2 shows the two available views for this document. Additional views can be added simply by defining additional style sheets.

Global Style Sheets

Figure 11-2. Comparison of the two different views available for this document.

Randomly Applying Style Sheets

The previous example demonstrates a technique that lets the user manually select style sheets, but there is no requirement that this selection be done by the user. You can write code that automatically applies a random style sheet so that each visit to the page displays the same contents in a different way. This simple technique makes a Web site appear more interesting and dynamic without having to continually change the contents.

The section "Adaptive Layout Techniques" later in this chapter demonstrates a technique for changing the appearance of a page based on the user's environment. In general, any event—whether user initiated, the result of some action, or even the result of a timer—can be used to modify the appearance of the document.

Media-Dependent Style Sheets

HTML 4.0 defines a mechanism for associating different style sheets with different types of media. Internet Explorer 4.0 supports two types of media: screen and print. You can define different style sheets that apply to the document when it is displayed on screen or printed. The following code demonstrates how to define three style sheets, one for printing, one for viewing on screen, and one for all views of the document:

<STYLE TYPE="text/css" MEDIA="screen">
   /* Applies only when the document is viewed on screen*/
   H1 {color:navy; text-align:center}
   P {margin-left:10pt}
</STYLE>
<STYLE TYPE="text/css" MEDIA="print">
   /* Applies only when the document is printed */
   H1 {color:black}
   P {margin-left:5pt}
</STYLE>
<STYLE TYPE="text/css" MEDIA="screen, print">
   /* Applies when the document is displayed on screen or printed */
   H2 {font-size:12pt}
</STYLE>

If the media attribute is omitted, the style sheet applies to all views of the document. The media attribute is a property on the styleSheet object and on the Style and Link elements. You can dynamically change this property to switch the media the style sheet applies to. The next section contains sample code that can determine which style sheets are currently being applied to the on-screen view of the document.

The styleSheet Object's cssText Property

The section "The cssText Property" earlier in this chapter introduced the cssText property as a style property that is exposed on each element. In addition, each styleSheet object exposes a read-only cssText property, which represents the global style sheet formatted as text. This property is very useful for quickly viewing the style sheet associated with the page. The following code placed at the end of the document outputs all the style sheets that are currently applied to the document:

<SCRIPT LANGUAGE="JavaScript">
   var ss = document.styleSheets;
   document.write("<PRE>");
   for (var intLoop = 0; intLoop < ss.length; intLoop++)
      // Style sheet is for the screen and not disabled.
      if ((("" == ss[intLoop].media) ||
            (-1 != ss[intLoop].media.indexOf("screen"))) &&
            (!ss[intLoop].disabled))
         document.write(ss[intLoop].cssText);
   document.write("</PRE>");
</SCRIPT>

The rules Collection

Every style sheet exposes its collection of rules. A rule is the combination of the style declaration (for example, color:red) and its selector (for example, H1). Using this collection, you can access and dynamically change the declaration. The selector is read-only. If a new selector is necessary, you must remove the rule and add a new rule to the style sheet. Rules are added and removed through the addRule and removeRule methods on the styleSheet object.

Each rule in the rules collection represents a single selector and declaration, regardless of how it was defined in the style sheet. The following example demonstrates how a style sheet with grouped selectors is exposed by the rules collection:

<STYLE TYPE="text/css">
   H1, H2, P EM {color:green}
</STYLE>
<SCRIPT LANGUAGE="JavaScript">
   var rules = document.styleSheets[0].rules;
   for (var intLoop = 0; intLoop < rules.length; intLoop++)
      document.write("Rule: " + rules[intLoop].selectorText +
         ", Style: " + rules[intLoop].style.cssText + "<BR>");
</SCRIPT>

The preceding code outputs three separate rules because the grouping is separated in the object model so that the individual styles can be accessed and changed more easily. This code also demonstrates two of the three properties available on each rule. The selectorText is a read-only property that represents the selector portion of the rule. The style property works the same as the style property exposed on the individual elements; it allows the style for the selector to be modified.

Adding and Removing Rules

The addRule method adds a new rule to the style sheet; the removeRule method removes an existing rule from the style sheet. By default, new rules are added to the end of the style sheet, taking precedence over all rules defined earlier. Because each style sheet is merged independently, a new rule added to the first style sheet has higher precedence than all the rules in that sheet, but it has lower precedence than the rules in any style sheets that follow. Therefore, to ensure that the rule takes precedence over existing rules, you must add the rule to the last style sheet specified in the document, as shown here:

var intSS = document.styleSheets.length;
if (0 < intSS) // Be sure there is a style sheet to add the rule to.
   document.styleSheets[intSS - 1].addRule("H1",
      "color:red; font-size:18pt");

When you need more control over the position of the rule in the style sheet, you can add the rule to the rules collection at a specified position by supplying an index as the last parameter to the method. This code adds a rule to the beginning of the style sheet:

var intSS = document.styleSheets.length;
if (0 < intSS) // Be sure there is a style sheet to add the rule to.
   document.styleSheets[intSS - 1].addRule("H1",
      "color:red; font-size:18pt", 0); // Add before the first rule.

In all cases, the addRule method returns an index representing where the rule was added into the rules collection. In this example, where the index is explicitly defined, the addRule method returns 0.

The removeRule method performs the reverse operation and returns the index of the rule removed. The following code demonstrates how to remove the first rule from the last style sheet:

var intSS = document.styleSheets.length;
if (0 < intSS) // Be sure there is a style sheet to remove 
               // the rule from.
   document.styleSheets[intSS - 1].removeRule(0);

Linked Style Sheets and Rules

All style sheets expose a readOnly property, which indicates whether the style sheet can be modified. For linked style sheets, this property returns true. However, linked style sheets allow rules to be added and modified. Changing a linked style sheet affects only the currently displayed instance of the document. Adding a rule to a linked style sheet does not cause the other documents that share that style sheet to update with the same rule. There is currently no way, short of adding the rule to each document, to dynamically change a style sheet shared by multiple documents.

Imported Style Sheets

You can use the @import statement in your style sheet to import another style sheet. Through the object model, you can dynamically access, add, and remove imported style sheets.

The imports contained within a style sheet are exposed by the imports collection, each element of which is another styleSheet object. An imported style sheet can further import another style sheet. Therefore, to allow you to determine what style sheet the import is contained within, the styleSheet object exposes a parentStyleSheet property, which returns the styleSheet object that defined the import. For top-level style sheets, this property returns a value of null.

The addImport method on the styleSheet object takes a string value representing the URL. According to the CSS specification, imported style rules always exist at the beginning of the style sheet and therefore at the beginning of the cascading order. Thus, any rules in imported style sheets have lower precedence than the rules already in the style sheet. The following code imports a style sheet named cool.css into the first style sheet in the styleSheets collection:

document.styleSheets[0].addImport("URL(`cool.css');");

Use the removeImport method to remove the import at the specified position in the imports collection. The following code removes the first import from the style sheet:

document.styleSheets[0].removeImport(0);

Adding New Style Sheets

Style sheets can be added to the document by using the createStyleSheet method on the document. By default, the createStyleSheet method adds a new style sheet to the end of the styleSheets collection. To add a new linked style sheet, supply a URL as the first argument; to specify where to insert the style sheet, supply an index as the second argument. If you need to create a nonlinked style sheet and insert it at a particular position in the styleSheets collection, pass null for the first argument.

Style Sheet Painter

The following example demonstrates how to dynamically modify the global style sheet of a document to quickly change the appearance of all elements of the same type. This demonstration uses a frameset in which the left frame contains a set of styles and the right frame contains contents to apply the styles to. The user selects a style from the left frame and then clicks on an element in the right frame; all elements of the same type as the one clicked on are immediately updated. The tag name of the element to which the style will be applied is displayed in the status bar. This example uses three files. The stylizer.htm file contains a frameset and most of the core code to transfer a style from the style frame to the contents frame. The styles.htm file contains a table of styles to choose from, and the contents.htm file contains the contents to apply the styles to.

This example uses the following techniques:

Figure 11-3 shows the style painter application as defined by the following code examples.

Global Style Sheets

Figure 11-3. The style painter application in action.

The stylizer.htm fileThe following frameset document divides the screen into two frames: the left pane displays a list of style options, and the right pane displays the document to apply the styles to. The code that handles communications between the two other documents is contained within this document.

<HTML>
   <HEAD>
      <TITLE>The Stylizer</TITLE>
      <SCRIPT LANGUAGE="JavaScript">
         window.curStyle = null;
         function selectStyle() {
            // Highlight the currently selected style cell.
            var el = this.parentWindow.event.srcElement;
            if ("TD" == el.tagName) {
               if (null != curStyle)
                  curStyle.className = "";
               curStyle = el;
               curStyle.className = "selected";
            }
         }
 
         function addStyle() {
            // Add a new rule to the document for the selected style.
            if (null != curStyle) { 
               var srcWin = this.parentWindow;
               var tag = srcWin.event.srcElement.tagName;
               srcWin.document.styleSheets[0].addRule(tag,
                  curStyle.style.cssText);
            }
         }

         function hookupEvents() {
            /* Bind each frame's click events to the appropriate
               function in this document. */
            window.styles.document.onclick = selectStyle;
            window.content.document.onclick = addStyle;
         }
      </SCRIPT>
   </HEAD>
   <FRAMESET ONLOAD="hookupEvents();" COLS="170, *">
      <FRAME SRC="styles.htm" NAME="styles">
      <FRAME SRC="content.htm" NAME="content">
   </FRAMESET>
</HTML>

The styles.htm fileThe following document contains a table of styles that the user can select and apply to the contents document. Adding more table cells to the table can extend the list of styles.

<HTML>
   <HEAD>
      <TITLE>Style List</TITLE>
      <STYLE TYPE="text/css">
         /* This style is used to highlight the user's selection. */
         .selected {border:2px black solid}
      </STYLE>
   </HEAD>
   <BODY>
      <P>Select a style and click on the document to apply it.</P>
      <!-- A cell's inline style specifies the style that can be
           applied in the contents document when the cell is selected.
           The style is simply copied over. -->
      <TABLE>
         <TR>
            <TD STYLE="background:white; color:black; font-size:12pt">
               Small White and Black
            </TD>
         </TR><TR>
            <TD STYLE="background:red; color:white; font-size:18pt">
               Big Red and White
            </TD>
         </TR><TR>
            <TD STYLE="background:navy; color:yellow; font-size:14pt">
               Medium Navy and Yellow
            </TD>
         </TR>
      </TABLE>
   </BODY>
</HTML>

The content.htm fileThe following sample document contains contents to which the selected styles are applied. The small script in this document is used to display the tag name of the element to which the style is to be applied in the status bar.

<HTML>
   <HEAD>
      <TITLE>Demo Contents</TITLE>
      <SCRIPT LANGUAGE="JavaScript">
         function updateStatus() {
            /* Display the name of the element the mouse is over.
               This is the element type to which the new style will
               be applied. */
            window.defaultStatus = event.srcElement.tagName;
         }
      </SCRIPT>
      <STYLE TYPE="text/css">
         /* Style block to add rules to */
      </STYLE>
   </HEAD>
   <BODY ONMOUSEOVER="updateStatus();">
      <H1>Demo Contents</H1>
      <P>Here are some demo <EM>contents</EM> to test the
         <EM>stylizer</EM> on.</P>
      <P>Select a style from the left pane, and click on
         <STRONG>text</STRONG> in this pane. The element you click
         and all elements of the same type will
         <STRONG>change</STRONG> to match that style.
      <P>This technique adds new rules to the style sheet for
         this document.
   </BODY>
</HTML>

Style Sheet Events

A styleSheet object is not created and added to the styleSheets collection until the entire style sheet is loaded, including the complete downloading of any linked or imported style sheets. For tracking the status of a style sheet, the Style and Link elements expose an onreadystatechange and an onload event. The readyState property returns a string that represents the current state of the element. These events and the readyState property are similar to the members of the same names on the document and window.

While a style sheet is being parsed, its readyState value is loading. Once the entire style sheet has been loaded, readyState changes to complete. Immediately prior to complete, the styleSheet object is created and added to the styleSheets collection. The onreadystatechange or onload event can be used to track when the style sheet becomes available. The onload event always occurs immediately following the onreadystatechange event, firing when the style sheet reaches the complete state. The following document demonstrates the ordering sequence:

<HTML>
   <HEAD>
      <TITLE>Style Sheet Events</TITLE>
      <STYLE TYPE="text/css"
            ONREADYSTATECHANGE=
               "alert(`readyState: ` + this.readyState);"
            ONLOAD="alert(`load event');">
         H1 {color:red}
      </STYLE>
   </HEAD>

   <BODY>
      <H1>Heading</H1>
   </BODY>
</HTML>

Alert boxes display the following messages in the order shown:

  1. readyState: loading
  2. readyState: complete
  3. load event

The style sheet is loaded synchronously into the document; while the style sheet is being loaded, the rest of the document is not parsed or rendered. One use for the onreadystatechange and onload events is to provide the user with status bar notifications of the status of the document, as shown here:

<HTML>
   <HEAD>
      <TITLE>readyState of the Document</TITLE>
      <SCRIPT LANGUAGE="JavaScript">
         function updateStatus(msg) {
            window.defaultStatus = msg;
         }
      </SCRIPT>
      <!-- Provide an update of the downloading
           of the style sheet. -->
      <LINK REL="styleSheet" TYPE="text/css"
         HREF="dhtml.css" TITLE="Default Sheet"
         ONREADYSTATECHANGE="updateStatus(`StyleSheet[` +
            this.title + `]: ` + this.readyState);">
      <SCRIPT LANGUAGE="JavaScript">
         // Let the user know the document is still parsing.
         updateStatus("Parsing: " + document.title);
      </SCRIPT>
   </HEAD>
   <BODY ONLOAD="updateStatus(``);">
      <H1>Status Tracking</H1>
   </BODY>
</HTML>

If the style sheet fails to load because the server times out or the file does not exist, neither the onload or the final onreadystatechange event that signifies the download is complete is fired. Currently, no error is generated and no explicit error event is available to track whether the linked style sheet failed to download. One work-around to solve this problem is to set a flag in the onload event handler. If this flag is not set, an error must have occurred during downloading of the style sheet:

<HTML>
   <HEAD>
      <TITLE>Tracking Download Errors</TITLE>
      <LINK REL="styleSheet" TYPE="text/css" HREF="dhtml.css"
         TITLE="Default Styles" ID="ss1"
         ONLOAD="this.downloaded = true; // Success!">
      <SCRIPT LANGUAGE="JavaScript">
         /* If the property does not exist, an error occurred.
            The property would be added to the element, not to the
            styleSheet object. */
         if (null == document.all.ss1.downloaded) 
            alert("Error downloading style sheet.");
      </SCRIPT>
   </HEAD>
   <BODY>
      <H1>Error Tracking</H1>
   </BODY>
</HTML>

[Содержание]