11.1 Table Formatting
Before you can start to worry about how cell borders are drawn and
tables sized, you need to delve into the fundamental ways in which
tables are assembled, and the ways that elements within the table are
related to each other. This is what is referred to as
table formatting, and it is
quite distinct from table layout: the latter is possible only after
the former has been completed.
11.1.1 Visually Arranging a Table
The first thing to understand is how
CSS defines the arranging of tables. While this knowledge may seem a
bit basic, it's key to understanding how best to
style tables.
CSS draws a distinction between
table elements and
internal table elements. In CSS, internal table elements generate
rectangular boxes that have content, padding, and borders, but do not
have margins. Therefore, it is not possible to
define the separation between cells by giving them margins. A
CSS-conformant browser will ignore any attempts to apply margins to
cells, rows, or any other internal table element (with the exception
of captions, which are discussed later in the chapter).
There are six rules for arranging tables. The basis of these rules is
a
"grid cell,"
which is one area between the grid lines on which a table is drawn.
Consider Figure 11-1, in which two tables are shown
along with their grid cells, which are indicated by the dashed lines
drawn over the tables.
In a simple two-by-two table, such as the left-hand table shown in
Figure 11-1, the grid cells correspond to the cells.
In a more complicated table, like the right-hand one in Figure 11-1, the edges of the grid cells correspond to the
cell borders of all the cells in the table, and cut through those
cells that span rows or columns.
These grid cells are largely theoretical constructs, and they cannot
be styled or even accessed through the document object model. They
simply serve as a way to describe how tables are assembled for
styling.
11.1.1.1 Table arrangement rules
Each row box encompasses a single row of
grid cells. All of the row boxes in a table fill the table from top
to bottom in the order they occur in the source document (with the
exception of any table header or table footer row boxes, which come
at the beginning and end of the table, respectively). Thus, the table
contains as many grid rows as there are row elements. A row group's box encompasses the same grid cells as
the row boxes it contains.
A column box encompasses one or more
columns of grid cells. All the column boxes are placed next to each
other in the order they occur. The first column box is on the left
for left-to-right languages, and on the right for right-to-left
languages. A column group's box encompasses the same grid cells
as the column boxes that it contains. Although
cells may span several rows or columns, CSS does not define how this
happens. It is instead left to the document language to define
spanning. Each spanned cell is a rectangular box one or more grid
cells wide and high. The top row of this rectangle is in the row that
is parent to the cell. The cell's rectangle must be
as far to the left as possible in left-to-right languages, but it may
not overlap any other cell box. It must also be to the right of all
cells in the same row that are earlier in the source document in a
left-to-right language. In right-to-left languages, a spanned cell
must be as far to the right as possible without overlapping other
cells, and must be to the left of all cells in the same row that
follow it in the document source.
A cell's box cannot
extend beyond the last row box of a table or row group. If the table
structure would cause this condition, the cell must be shortened
until it fits within the table or row group that encloses it.
|
The CSS specification discourages,
but does not prohibit, the positioning of table cells and other
internal table elements. Positioning a row that contains row-spanning
cells, for example, could dramatically alter the layout of the table
by removing the row from the table entirely, and thus removing the
spanned cells from consideration in the layout of other rows.
|
|
By definition,
grid cells are rectangular, but they do not all have to be the same
size. All the grid cells in a given grid column will be the same
width, and all the grid cells in a grid row will be the same height,
but the height of one grid row may be different than that of another
grid row. Similarly, grid columns may be of different widths.
With those basic rules in mind, a question may arise: how, exactly,
do you know which elements are cells and which are not?
We'll find out in the next section.
11.1.2 Table Display Values
In HTML, it's
easy to know which elements are parts of tables because the handling
of elements like tr and td is
built into browsers. In XML, on the other hand, there is no way to
intrinsically know which elements might be part of a table. This is
where a whole collection of values for display
come into play.
- Values
-
none | inline |
block | inline-block |
list-item | run-in |
table | inline-table |
table-row-group |
table-header-group | table-footer-group
| table-row |
table-column-group |
table-column | table-cell |
table-caption | inherit
- Initial value
-
inline
- Applies to
-
all elements
- Inherited
-
no
- Computed value
-
varies for floated, positioned, and root elements (see CSS2.1,
section 9.7); otherwise, as specified
- Note
-
the values compact and marker
appeared in CSS2 but were dropped from CSS2.1 due to a lack of
widespread support
|
In this chapter, we'll stick to the table-related
values, as the others (block,
inline, inline-block,
run-in, and list-item) are
discussed in other chapters. The table-related values can be
summarized as follows:
- table
-
This value specifies that an element
defines a block-level table. Thus, it defines a rectangular block
that generates a block box. The corresponding HTML element is, not
surprisingly, table.
- inline-table
-
This
value specifies that an element defines an inline-level table. This
means the element defines a rectangular block that generates an
inline box. The closest non-table analogue is the value
inline-block. The closest HTML element is
table, although, by default, HTML tables are not
inline.
- table-row
-
This
value specifies that an element is a row of cells. The corresponding
HTML element is the tr element.
- table-row-group
-
This
value specifies that an element groups one or more rows. The
corresponding HTML value is tbody.
- table-header-group
-
This value is
very much like table-row-group, except that for
visual formatting, the header row group is always displayed before
all other rows and row groups and after any top captions. In print,
if a table requires multiple pages to print, a user agent may repeat
header rows at the top of each page. The specification does not
define what happens if you assign
table-header-group to multiple elements. A header
group can contain multiple rows. The HTML equivalent is
thead.
- table-footer-group
-
This value is
very much like table-header-group, except that the
footer row group is always displayed after all other rows and row
groups and before any bottom captions. In print, if a table requires
multiple pages to print, a user agent may repeat footer rows at the
bottom of each page. The specification does not define what happens
if you assign table-footer-group to multiple
elements. This is equivalent to the HTML element
tfoot.
- table-column
-
This
value declares that an element describes a column of cells. In CSS
terms, elements with this display value are not
visually rendered, as if they had the value none.
Their existence is largely for the purposes of helping to define the
presentation of cells within the column. The HTML equivalent is the
col element.
- table-column-group
-
This
value declares that an element groups one or more columns. Like
table-column elements,
table-column-group elements are not rendered, but
the value is useful for defining presentation for elements within the
column group. The HTML equivalent is the colgroup element.
- table-cell
-
This
value specifies that an element represents a single cell in a table.
The HTML elements th and
td are both examples of
table-cell elements.
- table-caption
-
This
value defines a table's caption. CSS does not define
what should happen if multiple elements have the value
caption, but it does explicitly warn,
"...authors should not put more than one element
with `display: caption' inside a
table or inline-table element."
You can get a quick summary of the general effects of these values by
taking an excerpt from the example HTML 4.0 style sheet given in
Appendix C:
table {display: table;}
tr {display: table-row;}
thead {display: table-header-group;}
tbody {display: table-row-group;}
tfoot {display: table-footer-group;}
col {display: table-column;}
colgroup {display: table-column-group;}
td, th {display: table-cell;}
caption {display: table-caption;}
In XML, where elements will not have display semantics by default,
these values become quite useful. Consider the following markup:
<scores>
<headers>
<label>Team</label>
<label>Score</label>
</headers>
<game sport="MLB" league="NL">
<team>
<name>Reds</name>
<score>8</score>
</team>
<team>
<name>Cubs</name>
<score>5</score>
</team>
</game>
</scores>
This could be formatted in a tabular fashion using the following
styles:
scores {display: table;}
headers {display: table-header-group;}
game {display: table-row-group;}
team {display: table-row;}
label, name, score {display: table-cell;}
The various cells could then be styled as necessary—e.g.,
boldfacing the label elements and right-aligning
the scores.
|
While it's
theoretically possible to assign table-related
display values to any HTML element, Internet
Explorer does not support this capability.
|
|
11.1.2.1 Row primacy
CSS defines its table model as
"row primacy." In other words, the
model assumes that authors will create markup languages where rows
are explicitly declared. Columns, on the other hand, are derived from
the layout of the rows of cells. Thus, the first column is comprised
of all the first cells in each row, the second column of the second
cells, and so forth.
Row primacy is not a major issue in HTML, where the markup language
is already row-oriented. In XML, it has more of an impact because it
constrains the way in which authors can define table markup. Because
of the row-oriented nature of the CSS table model, a markup language
in which columns are the basis of table layout is not really possible
(assuming that the intent is to use CSS to present such documents).
The row primacy of the CSS model will also be seen throughout the
rest of the chapter as we explore the details of table presentation.
11.1.2.2 Columns
Although the CSS table model is row-oriented, columns do still play a
part in layout. A cell can belong to both contexts (row and column),
even if they are descended from row elements in the document source.
In CSS, however, columns and column groups can accept only four
styles: border, background,
width, and visibility.
In addition, each of these four properties has special rules that
apply only in the columnar context:
- border
-
Borders can be set for columns
and column groups only if the property
border-collapse has the value
collapse. In such circumstances, column and
column-group borders participate in the collapsing algorithm that
sets the border styles at each cell edge. (See Section 11.2.2 later in this chapter.)
- background
-
The background of a column or
column group will be visible only in cells where both the cell and
its row have transparent backgrounds. (See Section 11.1.4 later in this chapter.)
- width
-
The width
property defines the minimum width of the column
or column group. The content of cells within the column (or group)
may force the column to become wider.
- visibility
-
If
the value of visibility for a column or column
group is collapse, then none of the cells in the
column (or group) are rendered. Cells that span from the collapsed
column into other columns are clipped, as are cells that span from
other columns into the hidden column. Furthermore, the overall width
of the table is reduced by the width the column would have taken up.
A declaration of any value for visibility other
than hidden for a column or column group is
ignored.
11.1.3 Anonymous Table Objects
There is the possibility that a
markup language might not contain enough elements to fully represent
tables as they are defined in CSS, or that an author will forget to
include all the necessary elements. For example, consider this XHTML:
<table>
<td>Name:</td>
<td><input type="text"></td>
</table>
You might glance at this markup and assume that it defines a two-cell
table of a single row, but structurally, there is no element defining
a row (because the tr is missing).
To cover such possibilities, CSS defines a mechanism for inserting
"missing" table components as
anonymous objects. For a basic example of how this works,
let's revisit our missing-row XHTML example. In CSS
terms, what effectively happens is that an anonymous
table-row object is inserted between the
table element and its descendant table cells:
<table>
[anonymous table-row object begins]
<td>Name:</td>
<td><input type="text"></td>
[anonymous table-row object ends]
</table>
A visual representation of this process is given in Figure 11-2.
Seven different kinds of anonymous-object insertions can occur in the
CSS table model. These seven rules are, like inheritance and
specificity, an example of a mechanism that attempts to impose
intuitive sense on the way CSS behaves.
11.1.3.1 Object insertion rules
If a table-cell element's parent
is not a table-row element, then an anonymous
table-row object is inserted between the
table-cell element and its parent. The inserted
object will include all consecutive siblings of the
table-cell element. Consider the following styles
and markup: system {display: table;}
name, moons {display: table-cell;}
<system>
<name>Mercury</name>
<moons>0</moons>
</system> The anonymous table-row object is inserted between
the cell elements and the system element, and it encloses both the
name and system elements. The same holds true even if the parent element is a
table-row-group. To extend the example, assume
that the following applies: system {display: table;}
planet {display: table-row-group;}
name, moons {display: table-cell;}
<system>
<planet>
<name>Mercury</name>
<moons>0</moons>
</planet>
<planet>
<name>Venus</name>
<moons>0</moons>
</planet>
</system> In this example, both sets of cells will be enclosed in an anonymous
table-row object that is inserted between them and
the planet elements. If a table-row element's parent
is not a table, inline-table,
or table-row-group element, then an anonymous
table element is inserted between the
table-row element and its parent. The inserted
object will include all consecutive siblings of the
table-row element. Consider the following styles
and markup: docbody {display: block;}
planet {display: table-row;}
<docbody>
<planet>
<name>Mercury</name>
<moons>0</moons>
</planet>
<planet>
<name>Venus</name>
<moons>0</moons>
</planet>
</docbody> Because the display value of the
planet elements' parent is
block, the anonymous table
object is inserted between the planet elements and
the docbody element. This object will enclose both
planet elements because they are consecutive
siblings. If a table-column element's
parent is not a table,
inline-table, or
table-column-group element, then an anonymous
table element is inserted between the
table-column element and its parent. This is much
the same as the table-row rule just discussed,
except for its column-oriented nature. If the parent element of a table-row-group,
table-header-group,
table-footer-group,
table-column-group, or
table-caption element is not a
table element, then an anonymous
table object is inserted between the element and
its parent. If a child element of a table or
inline-table element is not a
table-row-group,
table-header-group,
table-footer-group, table-row,
or table-caption element, then an anonymous
table-row object is inserted between the
table element and its child element. This
anonymous object spans all of the consecutive siblings of the child
element that are not table-row-group,
table-header-group,
table-footer-group, table-row,
or table-caption elements. Consider the following
markup and styles: system {display: table;}
planet {display: table-row;}
name, moons {display: table-cell;}
<system>
<planet>
<name>Mercury</name>
<moons>0</moons>
</planet>
<name>Venus</name>
<moons>0</moons>
</system> Here, a single anonymous table-row object will be
inserted between the system element and the second
set of name and moons elements.
The planet element is not enclosed by the
anonymous object because its display is
table-row. If a child element of a table-row-group,
table-header-group, or
table-footer-group element is not a
table-row element, then an anonymous
table-row object is inserted between the element
and its child element. This anonymous object spans all of the
consecutive siblings of the child element that are not
table-row objects themselves. Consider the
following markup and styles: system {display: table;}
planet {display: table-row-group;}
name, moons {display: table-cell;}
<system>
<planet>
<name>Mercury</name>
<moons>0</moons>
</planet>
<name>Venus</name>
<moons>0</moons>
</system> In this case, each set of name and
moons elements will be enclosed in an anonymous
table-row element. For the second set, the
insertion happens in accord with Rule 5. For the first set, the
anonymous object is inserted between the planet
element and its children because the planet
element is a table-row-group element. If a child element of a table-row element is not a
table-cell element, then an anonymous
table-cell object is inserted between the element
and its child element. This anonymous object encloses all consecutive
siblings of the child element that are not
table-cell elements themselves. Consider the
following markup and styles: system {display: table;}
planet {display: table-row;}
name, moons {display: table-cell;}
<system>
<planet>
<name>Mercury</name>
<num>0</num>
</planet>
</system> Because the element num does not have a
table-related display value, an anonymous
table-cell object is inserted between the
planet element and the num
element. This behavior also extends to the encapsulation of anonymous inline
boxes. Suppose that the num element was not
included: <system>
<planet>
<name>Mercury</name>
0
</planet>
</system> The 0 would still be enclosed in an anonymous
table-cell object. To further illustrate this
point, here is an example adapted from the CSS specification: example {display: table-cell;}
row {display: table-row;}
hi {font-weight: 900;}
<example>
<row>This is the <hi>top</hi> row.</row>
<row>This is the <hi>bottom</hi> row.</row>
</example> Within each row element, the text fragments and
hi element are enclosed in an anonymous
table-cell object.
11.1.4 Table Layers
For the assembly of a
table's presentation, CSS defines six individual
"layers" on which the various
aspects of a table are placed. Figure 11-3 shows
these layers.
Basically, the styles for each aspect of the table are drawn on their
individual layers. Thus, if
the table element has a green background and a
1-pixel black border, then those styles are drawn on the lowest
layer. Any styles for the column groups are drawn
on the next layer up, the columns
themselves on the layer above that, and so on. The top layer, which
corresponds to the
table cells, is drawn last.
For the most part, this is simply a logical process; after all, if
you declare a background color for table cells, you would want that
drawn over the background for the table element.
The most important point revealed by Figure 11-3 is
that column styles come below row styles, so a
row's background will
overwrite a column's background.
It is important to remember that, by default, all elements have
transparent backgrounds. Thus, in the following markup, the
table element's background will
be visible "through" cells, rows,
columns, and so forth that do not have a background of their own, as
illustrated in Figure 11-4:
<table style="background: #888;">
<tr>
<td>hey</td>
<td style="background: #CCC;">there</td>
</tr>
<tr>
<td>what's</td>
<td>up?</td>
</tr>
<tr style="background: #AAA;">
<td>tiger</td>
<td style="background: #CCC;">lilly</td>
</tr>
</table>
11.1.5 Captions
A table caption is about what
you'd expect: a short bit of text that describes the
nature of the table's contents. A chart of stock
quotes for the fourth quarter of 2003, therefore, might have a
caption element whose contents read "Q4 2003 Stock
Performance." With the property
caption-side, you can
place this element either above or below the table, regardless of
where the caption appears in the table's structure.
(In HTML, the caption element can appear only
after the opening table element, but other
languages may have different rules.)
- Values
-
top | bottom
- Initial value
-
top
- Applies to
-
elements with the display value
table-caption
- Inherited
-
no
- Computed value
-
as specified
- Note
-
the values left and right
appeared in CSS2 but were dropped from CSS2.1 due to a lack of
widespread support
|
Captions are a bit odd, at least in visual terms. The CSS
specification states that a caption is formatted as if it were a
block box placed immediately before (or after) the
table's box, with a couple of exceptions. The first
is that the caption can still inherit values from the table, and the
second is that a user agent ignores a caption's box
when considering what to do with a run-in element that precedes the
table. Therefore, a run-in element that comes before a table will not
run into a top caption, nor into the table, but will instead be
treated as if its display value were
block.
A simple example should suffice to illustrate most of the important
aspects of caption presentation. Consider the following, illustrated
in Figure 11-5:
caption {background: gray; margin: 1em 0;
caption-side: top;}
table {color: white; background: black; margin: 0.5em 0;}
The text in the caption element inherits the
color value white from the
table, while the caption gets its own background. The separation
between the table's outer border edge and the
caption's outer margin edge is one em, as the top
margin of the table and bottom margin of the caption have collapsed,
as described in Chapter 7. Finally, the width
of the caption is based on the content width of the
table element, which is considered to be the
containing block of the caption. These same results would occur if
you change the value of caption-side to
bottom, except that the caption would be placed
after the table's box, and collapsing would occur
between the top margin of the caption and the bottom margin of the
table.
For the most part, captions are styled just like any block-level
element; they can be padded, have borders, be given backgrounds, and
so on. For example, if you need to change the horizontal alignment of
text within the caption, you use the property
text-align. Thus,
to right-align the caption in the previous example, you would write:
caption {background: gray; margin: 1em 0;
caption-side: top; text-align: right;}
|