Приглашаем посетить
Естествознание (es.niv.ru)

Programming the Table Element

Programming the Table Element

Tables are used in HTML for displaying tabular data and to provide greater control over the layout and position of elements in the document. Tables consist of rows; each row contains any number of cells. Dynamic HTML exposes a custom object model on tables that provides easy access to the underlying rows and cells within the table.

Tables were greatly enhanced in Internet Explorer 3.0 to support features that are now included in HTML 4.0. The THead, TBody, and TFoot elements were added to define the header, body, and footer sections of the table, and the Col and ColGroup elements provide greater control over columns. When used appropriately, these elements can improve the performance of the table, especially by defining the widths of the columns and providing more control over the rendering of borders. The Table element exposes a powerful object model for dynamically manipulating tables.

The table Object

Every Table element exposes rich information about its contents. The table object provides access to the three different sections of the table: THead, TBody, and TFoot. A table can have only one THead and TFoot but any number of TBody elements. Therefore, the object model exposes a single tHead and tFoot property and a tBodies collection. If a table does not explicitly define any sections, an implicit TBody element is created and added to the tBodies collection.

If the table happens to contain multiple THead or TFoot sections, the properties reference the first section encountered, and all remaining sections are exposed by the tBodies collection.

The table object exposes methods for creating and deleting THead, TFoot, and Caption elements. (There is currently no method to insert additional TBody elements into the table.) These methods are listed in the following table.


Method Description
createTHead(), createTFoot(), createCaption() Creates and returns the specified section if one does not exist. If the section already exists, rather than create another, the method returns the existing section.
deleteTHead(), deleteTFoot(), deleteCaption() Deletes the specified section and its rows from the table if the section exists.
insertRow([index]) Inserts a row into the table before the specified index. The row is added to the same section as the row currently specified by the index. If no index is specified, the row is added to the end of the table in the same section as the existing last row. This method returns the row that was inserted.
deleteRow(index) Deletes the row at the specified index from the table.

The table object also exposes a rows collection. This rows collection represents every row in the table, independent of what section contains them. To determine what section contains a row, you can examine the parentElement property of the individual row. In addition, each section exposes a rows collection that represents the rows contained in that section.

The rows and cells Collections

The table object exposes the relationships between the table's rows and cells. As mentioned, the rows collection on the table object contains every TR element in the table, and the rows collections on the tHead, tBody, and tFoot objects contain the TR elements in their respective sections. Each row subsequently exposes a cells collection that references the TD or TH elements within the row. The rows and cells collections expose the same tags and item methods that are available on the other element collections. You can use an element's id property to look it up directly in a rows or cells collection.

Programming the rows Collection

The rows collection on the table object ignores whether a row is in the head, body, or foot of the table, but the TR element's relationship to its parent element is still maintained:

<TABLE ID="myTable">
   <THEAD>
      <TR ID="header"><TH>City</TH><TH>State</TH></TR>
   </THEAD>
   <TBODY>
      <TR><TD>Issaquah</TD><TD>Washington</TD></TR>
      <TR><TD>Seattle</TD><TD>Washington</TD></TR>
   </TBODY>
</TABLE>

In this example, the rows collection of myTable contains the three rows in the table. The parentElement property of an individual row can be examined to determine whether the row is inside a TBody or a THead element:

document.all.myTable.rows.length                     // 3
document.all.myTable.THead.rows.length               // 1
document.all.myTable.rows[0].parentElement.tagName   // THEAD
document.all.myTable.rows[1].parentElement.tagName   // TBODY

You can easily determine any row's position in the table. Three of the row's properties represent the row's zero-based index in the entire document, in the table, and in a section. The sourceIndex property represents the element's location in the document. This property, which all elements expose, is described in Chapter 8, "Scripts and Elements." The rowIndex property represents the index of the row in the entire table, and the sectionRowIndex property represents the index of the row in its section. In the previous example, the row containing Seattle has a rowIndex value of 2 and a sectionRowIndex of 1. (Its sourceIndex value depends on where the table appears in the document.)

Each row also provides access to its cells through a cells collection. The insertCell and deleteCell methods add and remove cells in the row. These methods work in the same manner as the insertRow and deleteRow methods. The insertCell method takes an optional parameter, the index of the cell before which the new cell is to be inserted, and returns the inserted cell. The deleteCell method takes the index of the cell to delete. The following code shows how to access and manipulate cells in the previous table:

document.all.myTable.rows[0].cells.length  // 2 cells
document.all.header.cells.length   // 2 cells, accessed through the ID
document.all.header.deleteCell(0); // Delete first cell in header row.

Each cell has a sourceIndex and a cellIndex property. The cellIndex property represents the index of the cell in the row.

The ROWSPAN and COLSPAN Attributes

The rows collections correspond to the HTML structure that defines the table. Therefore, even if a cell spans multiple rows, it is exposed only on the row that defines the cell. The following code demonstrates this relationship by flattening out access to a table that has a number of cells spanning multiple columns and rows:

<HTML>
   <HEAD>
      <TITLE>HTML Rows and Cells</TITLE>
   </HEAD>
   <BODY>
      <TABLE BORDER ID="tbl1">
         <CAPTION>Sample Table</CAPTION>
         <TR><TD ROWSPAN=3>0, 0</TD>
            <TD COLSPAN=2>0, 1</TD><TD>0, 2</TD></TR>
         <TR><TD>1, 0</TD><TD ROWSPAN=2 COLSPAN=2>1, 1</TD></TR>
         <TR><TD>2, 0</TD></TR>
      </TABLE>
      <SCRIPT LANGUAGE="JavaScript">
         // Output information about the table above.
         document.write("<H2>Table Information</H2>");
         with (document.all.tbl1) {
            for (var intRows=0; intRows < rows.length; intRows++)
               document.write("Row " + intRows + " has " +
                  rows[intRows].cells.length + " cell(s).<BR>");
            document.write("<P>Here is the same table without " +
               "any cells spanning multiple rows or columns:");
            document.write("<TABLE BORDER>");
            for (var intRows = 0; intRows < rows.length; intRows++) {
               document.write("<TR>");
               for (var intCells = 0;
                     intCells < rows[intRows].cells.length;
                     intCells++)
                  document.write("<TD>" + intRows + "," + intCells +
                     "</TD>");
               document.write("</TR>");
            }
            document.write("</TABLE>");
         }
      </SCRIPT>
   </BODY>
</HTML>

Figure 9-5 displays the HTML representation of this table. The rows and cells are defined by the underlying source, independent of how the table is actually rendered. The numbers in a cell represent the index of its row in the rows collection, followed by the index of its cells in the cells collection. The second table provides a view of the table with the ROWSPAN and COLSPAN attributes removed. The corresponding cells have the same indexes in both tables.

Programming the Table Element

Figure 9-5.

Spanning cells and the collections that contain them.

You can modify the colSpan and rowSpan properties to dynamically change the table's layout. Changing these properties does not cause the rows or cells collections to change. The only way to affect the collections is to explicitly add or remove sections, rows, or cells from the table using the insert and delete methods.

The onresize Event

The table exposes an onresize event that is fired whenever the table is resized. This event fires when any cell changes in size. A script can change the size of a cell by changing its height or width property or by changing its contents. No matter how many cells may change in size due to a single action, the onresize event is fired only once on the table itself.

Global Style Sheets

In general, CSS is not inherited by the contents of a table cell. This fact follows from historical practice with regard to HTML formatting elements. For example, specifying a Font element around a table does not cause that font to be used by the table contents. When style sheets were introduced, this rule needed to be carried forward to ensure that existing pages did not break. Therefore, when style sheets are required on a table, they should be specified on the table or table cells directly to ensure that they are applied to the contents.

Creating a Calendar

The following code example demonstrates how to manipulate a table using the rows and cells collections. A script generates most of the document using the document.write method.

<HTML>
   <HEAD>
      <TITLE>Calendar</TITLE>
      <STYLE TYPE="text/css">
         .today {color:navy; font-weight:bold}
         .days {font-weight:bold}
      </STYLE>
      <SCRIPT LANGUAGE="JavaScript">
         // Initialize arrays.
         var months = new Array("January", "February", "March",
            "April", "May", "June", "July", "August", "September",
            "October", "November", "December");
         var daysInMonth = new Array(31, 28, 31, 30, 31, 30, 31, 31,
            30, 31, 30, 31);
         var days = new Array("Sunday", "Monday", "Tuesday",
            "Wednesday", "Thursday", "Friday", "Saturday");

         function getDays(month, year) {
            // Test for leap year when February is selected.
            if (1 == month)
               return ((0 == year % 4) && (0 != (year % 100))) ||
                  (0 == year % 400) ? 29 : 28;
            else
               return daysInMonth[month];
         }

         function getToday() {
            // Generate today's date.
            this.now = new Date();
            this.year = this.now.getYear() + 1900; // Relative
                                                   // to 1900
            this.month = this.now.getMonth();
            this.day = this.now.getDate();
         }

         // Start with a calendar for today.
         today = new getToday();

         function newCalendar() {
            today = new getToday();
            var parseYear = parseInt(document.all.year
               [document.all.year.selectedIndex].text) - 1900;
            var newCal = new Date(parseYear,
               document.all.month.selectedIndex, 1);
            var day = -1;
            var startDay = newCal.getDay();
            var daily = 0;
            if ((today.year == newCal.getYear() + 1900) &&
                  (today.month == newCal.getMonth()))
               day = today.day;
            // Cache the table's tBody element named dayList.
            var tableCal = document.all.calendar.tBodies.dayList;
            var intDaysInMonth =
               getDays(newCal.getMonth(), newCal.getYear() + 1900);
            for (var intWeek = 0; intWeek < tableCal.rows.length;
                  intWeek++)
               for (var intDay = 0;
                     intDay < tableCal.rows[intWeek].cells.length;
                     intDay++) {
                  var cell = tableCal.rows[intWeek].cells[intDay];

                  // Start counting days.
                  if ((intDay == startDay) && (0 == daily))
                     daily = 1;

                  // Highlight the current day.
                  cell.className = (day == daily) ? "today" : "";

                  // Output the day number into the cell.
                  if ((daily > 0) && (daily <= intDaysInMonth))
                     cell.innerText = daily++;
                  else
                     cell.innerText = "";
               }
         }

         function getDate() {
            // This code executes when the user clicks on a day
            // in the calendar.
            if ("TD" == event.srcElement.tagName)
               // Test whether day is valid.
               if ("" != event.srcElement.innerText)
                  alert(event.srcElement.innerText);
         }
      </SCRIPT>
   </HEAD>
   <BODY ONLOAD="newCalendar()">
      <TABLE ID="calendar">
         <THEAD>
            <TR>
               <TD COLSPAN=7 ALIGN=CENTER>
                  <!-- Month combo box -->
                  <SELECT ID="month" ONCHANGE="newCalendar()">
                     <SCRIPT LANGUAGE="JavaScript">
                        // Output months into the document.
                        // Select current month.
                        for (var intLoop = 0; intLoop < months.length;
                              intLoop++)
                           document.write("<OPTION " +
                              (today.month == intLoop ?
                                 "Selected" : "") + ">" +
                              months[intLoop]);
                     </SCRIPT>
                  </SELECT>

                  <!-- Year combo box -->
                  <SELECT ID="year" ONCHANGE="newCalendar()">
                     <SCRIPT LANGUAGE="JavaScript">
                        // Output years into the document.
                        // Select current year.
                        for (var intLoop = 1995; intLoop < 2000;
                              intLoop++)
                           document.write("<OPTION " +
                              (today.year == intLoop ?
                                 "Selected" : "") + ">" +
                              intLoop);
                     </SCRIPT>
                  </SELECT>
               </TD>
            </TR>
            <TR CLASS="days">
               <!-- Generate column for each day. -->
               <SCRIPT LANGUAGE="JavaScript">
                  // Output days.
                  for (var intLoop = 0; intLoop < days.length;
                        intLoop++)
                     document.write("<TD>" + days[intLoop] + "</TD>");
               </SCRIPT>
            </TR>
         </THEAD>
         <TBODY ID="dayList" ALIGN=CENTER ONCLICK="getDate()">
            <!-- Generate grid for individual days. -->
            <SCRIPT LANGUAGE="JavaScript">
               for (var intWeeks = 0; intWeeks < 6; intWeeks++) {
                  document.write("<TR>");
                  for (var intDays = 0; intDays < days.length;
                        intDays++)
                     document.write("<TD></TD>");
                  document.write("</TR>");
               }
            </SCRIPT>
         </TBODY>
      </TABLE>
   </BODY>
</HTML>

The contents of the two combo boxes that provide the month and year lists are generated through script from internal arrays that track the months and days available to the calendar. The code also ensures that the current month and year are initially selected when the document loads. The table that defines the calendar is itself generated by a script that generates the 42 cells using two nested loops. Once the page is loaded, the newCalendar function is called and automatically walks through and fills in the cells of the table's tBody element with the current month's calendar.

Figure 9-6 shows the calendar example in action.

Programming the Table Element

Figure 9-6. A Dynamic HTML calendar.

This example also includes a simple click event handler that executes when the user clicks on any date in the calendar. Currently the handler does nothing more than display the date the user clicked, but it demonstrates how the calendar can be easily extended to be more interactive and useful to an application.

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