HTML5/tabular-data

From HTML5 Chinese Interest Group Wiki
Jump to: navigation, search

表格数据

table元素

分类:
流式内容
使用场景:
需要流式内容处。
内容模型:
一个可选的caption元素,跟随零或多个colgroup元素,可选的跟随一个thead元素,可选的跟随一个tfoot元素,跟随零至多个tbody元素或一至多个tr元素,可选的跟随一个tfoot元素(但不能只拥有一个tfoot元素作为其唯一的孩子)。
允许的属性:
全局属性
border
DOM接口:
interface HTMLTableElement : HTMLElement { attribute HTMLTableCaptionElement caption; HTMLElement createCaption(); void deleteCaption(); attribute HTMLTableSectionElement tHead; HTMLElement createTHead(); void deleteTHead(); attribute HTMLTableSectionElement tFoot; HTMLElement createTFoot(); void deleteTFoot(); readonly attribute HTMLCollection tBodies; HTMLElement createTBody(); readonly attribute HTMLCollection rows; HTMLElement insertRow(in optional long index); void deleteRow(in long index); attribute DOMString border; };

table元素以表格的形式表现超过一维的数据。

table元素是表格模型的一部分。

表格拥有由它的后裔所给出的行、列以及单元格。行和列组成一个网格;一个表格的单元格必须在不重叠的情况下完全覆盖这些网格。

注:确定该一致性规则是否被满足的精确规则在表格模型的描述中给出。

编码人员被鼓励提供更多的信息来描述复杂的表格。对于如何提供这样的信息的指导意见将在下面给出。

如果一个table元素拥有一个summary属性,并且用户代理没有将该表格分类为布局表格,则用户代理可以将该属性的内容展示给最终用户。

表格不应该被用作布局页面。从历史上看,很多Web编码人员在HTML中使用表格来控制他们的页面布局,这导致很难从这些文档中提取表格中的数据。特别是对于无障碍用户工具,例如屏幕阅读器,将可能很难在使用表格进行布局的页面中进行导航。如果一个表格将被用于布局,它必须使用role="presentation"属性进行标记,以便用户代理能够正确地将表格表现为一个辅助技术、正确地向工具传递编码人员希望从文档中提取表格数据的意图。

注:存在多种使用HTML表格进行布局的替代品,其中最主要的方法是使用CSS定位以及CSS表格模型。

border属性可以被指定于table元素之上,用来明确的表明该table元素并非用于布局用途。如果指定了该属性,该属性的值必须为空字符串或值“1”。该属性用于在某些用户代理上指定绘制在表格中单元格周围的边界。

表格可以更加复杂,以便理解和浏览。如果用户代理没有将表格分类为布局表格,则为了方便用户,用户代理应该清晰地描绘(原文拼写错误?dilineate -> delineate)出表格中的每一个单元格。

注:作者及实现人员被鼓励考虑使用某些表格布局技术进行描述,以便表格能够被用户轻松的浏览。

用户代理,特别是那些对任意内容进行表格分析的用户代理,被鼓励寻找一种探索法,用于确定那些表格包含实际的内容,那些表格只是被用于布局。本规范不会定义精确的探索法,但是下表中的内容被建议用作可能的指标。

特性 指标
使用了值为presentation(展示)的role(角色)属性 可能为布局表格
使用了值为不符合规范的0的border属性 可能为布局表格
使用了值为0的不符合规范的callspacingcellpadding属性 可能为布局表格
使用了captiontheadth元素 可能为非布局表格
使用了headersscope属性 可能为非布局表格
使用了值不为0的border属性 可能为非布局表格
使用CSS明确显式的设置的边框 可能为非布局表格
使用了summary属性 没有好的指标(布局表格及非布局表格都曾被给出该属性)
此区块中不是标准描述,实现要求在下面给出。table . caption [ = value ]

返回表格的caption元素。

能够被设置,用于替换caption元素。如果新的值不是一个caption元素,则抛出一个HIERARCHY_REQUEST_ERR异常。

caption = table . createCaption()

确保表格拥有一个caption元素,并且返回该元素。

table . deleteCaption()

确保表格不包含caption元素。

table . tHead [ = value ]

返回表格的thead元素。

能够被设置,用于替换thead元素。如果新的值不是一个thead元素,则抛出一个HIERARCHY_REQUEST_ERR异常。

thead = table . createTHead()

确保表格拥有一个thead元素,并且返回该元素。

table . deleteTHead()

确保表格不包含thead元素。

table . tFoot [ = value ]

返回表格的tfoot元素。

能够被设置,用于替换tfoot元素。如果新的值不是一个tfoot元素,则抛出一个HIERARCHY_REQUEST_ERR异常。

tfoot = table . createTFoot()

确保表格拥有一个tfoot元素,并且返回该元素。

table . deleteTFoot()

确保表格不包含tfoot元素。

table . tBodies

返回一个表格的tbody元素的HTMLCollection

tbody = table . createTBody()

创建一个tbody元素,将其插入到表格之中,并返回该元素。

table . rows

返回一个表格的tr元素的HTMLCollection

tr = table . insertRow(index)

创建一个tr元素,如果需要则还需创建一个tbody元素,将他们插入到表格中由参数所指定的位置,并且返回所创建的tr元素。

位置是相对于表格中的行的。索引-1等价于在表格的结尾处插入元素。

如果给出的位置小于-1或大于表格行数,则抛出一个INDEX_SIZE_ERR异常。

table . deleteRow(index)

删除表格中指定位置的tr元素。

位置是相对于表格中的行的。索引-1等价于删除表格结尾处的元素。

如果给出的位置小于-1或大于表格行数,则抛出一个INDEX_SIZE_ERR异常。

对于caption IDL属性,在获取时,若table元素中存在caption元素作为其子元素,必须返回其中的第一个元素,否则返回null。在设置时,如果新值是一个caption元素,则若table元素中存在caption元素作为其子元素则必须移除其中的第一个元素,并且必须将新值作为第一个节点插入到table元素之中。如果新值不是一个caption元素,则必须抛出一个HIERARCHY_ERQUEST_ERR DOM异常。

table元素中存在caption元素,则createCaption()方法必须返回其中的第一个元素,否则必须创建一个新的caption元素,并将其作为第一个节点插入到table元素之中,并返回该元素。

table元素中存在caption元素,则deleteCaption()方法必须删除其中的第一个元素。

对于tHead IDL属性,在获取时,如果table元素中存在thead元素作为其子元素,必须返回其中的第一个元素,否则返回null。在设置时,如果新值是一个thead元素,则若table元素中存在thead元素作为其子元素则必须移除其中的第一个元素,并且将新值插入到table元素中除了caption元素或colgroup元素之外的第一个元素之前。如果table元素中不存在caption元素或colgroup元素之外的元素作为其子元素,则将新值插入到表格的结尾。如果新值不是一个thead元素,则必须抛出一个HIERARCHY_ERQUEST_ERR DOM异常。

table元素中存在thead元素,则createTHead()方法必须返回其中的第一个元素,否则必须创建一个新的thead元素,并将其插入到table元素中除了caption元素或colgroup元素之外的第一个元素之前。如果table元素中不存在caption元素或colgroup元素之外元素作为其子元素,则将新的thead元素插入到表格的结尾。并返回新的thead元素。

table元素中存在thead元素,则deleteTHead()方法必须删除其中的第一个元素。

对于tFoot IDL属性,在获取时,如果table元素中存在tfoot元素作为其子元素,必须返回其中的第一个元素,否则返回null。在设置时,如果新值是一个tfoot元素,则若table元素中存在tfoot元素作为其子元素则必须移除其中的第一个元素,并且将新值插入到table元素中除了caption元素、colgroup元素或thead元素之外的第一个元素之前。如果table元素中不存在caption元素、colgroup元素或thead元素之外的元素作为其子元素,则将新值插入到表格的结尾。如果新值不是一个tfoot元素,则必须抛出一个HIERARCHY_ERQUEST_ERR DOM异常。

table元素中存在tfoot元素,则createTFoot()方法必须返回其中的第一个元素,否则必须创建一个新的tfoot元素,并将其插入到table元素中除了caption元素、colgroup元素或thead元素之外的第一个元素之前。如果table元素中不存在caption元素、colgroup元素或thead元素之外的元素作为其子元素,则将新的tfoot元素插入到表格的结尾。并返回新的tfoot元素。

table元素中存在tfoot元素,则deleteTFoot()方法必须删除其中的第一个元素。

tBodies属性必须返回一个跟为table节点的HTMLCollection,其过滤器仅匹配table元素子元素中的tbody子元素。

createTBody()方法必须创建一个新的tbody元素,如果table元素中存在tbody元素则将新元素插入到最后一个tbody元素之后,如果table元素中不存在tbody元素则将新元素插入到table元素的结尾处。并返回新的tbody元素。

rows属性必须返回一个跟为table节点的HTMLCollection,其过滤器仅匹配table元素子元素中的tr元素或者作为table元素子元素的thead元素、tbody元素或tfoot元素子元素中的tr元素。集合中的元素必须按照树形结构顺去排序,其中最先包含thead元素的子元素,之后跟随table元素或tbody元素的子元素,最后跟随tfoot元素的子元素。

insertRow(index)方法的行为由表格的状态决定。当该方法被调用时,必须使用下列条件中第一个能够描述表格组昂头的项目和index参数执行该方法:

  • 如果index小于-1或大于rows集合中的元素数:
    方法必须抛出一个INDEX_SIZE_ERR异常。
  • 如果rows集合中没有元素,且table中没有tbody
    方法必须创建一个tbody元素,之后创建一个tr元素,之后将tr元素追加到tbody元素之中,之后将tbody元素追加到table元素之中,最后返回tr元素。
  • 如果rows集合中没有元素:
    方法必须创建一个tr元素,将其追加到表格中最后一个tbody元素之中,并且返回tr元素。
  • 如果index丢失、等于-1或等于rows集合中的元素数:
    方法必须创建tr元素,并且将其追加到rows集合中最后一个tr元素的父元素之中。之后,返回新创建的tr元素。
  • 否则:
    方法必须创建一个tr元素,将其插入到同一个父元素的rows集合中第indextr元素之前,最终必须返回新创建的tr元素。

当调用deleteRow(index)方法时,用户代理必须执行下列步骤:

  1. 如果index等于-1,则index必须被设为rows集合中的元素数(原文中the number if items in the rows collection,应为the number of items in the rows collection),最小为壹。
  2. 如果index小于零,或大于等于rows集合中的元素数,方法必须抛出一个INDEX_SIZE_ERR异常,并终止这些步骤。
  3. 否则,方法必须移除其父元素的rows集合中的第index个元素。

border IDL属性必须反映同名的内容属性。

下面的例子展示了一个被用于标记一个数独游戏的表格。我们看到,这个表格中没有表头,对于这样的表格,它是不必要的。

<section>
 <style scoped>
  table { border-collapse: collapse; border: solid thick; }
  colgroup, tbody { border: solid medium; }
  td { border: solid thin; height: 1.4em; width: 1.4em; text-align: center; padding: 0; }
 </style>
 <h1>今天的数独游戏</h1>
 <table>
  <colgroup><col><col><col>
  <colgroup><col><col><col>
  <colgroup><col><col><col>
  <tbody>
   <tr> <td> 1 <td>   <td> 3 <td> 6 <td>   <td> 4 <td> 7 <td>   <td> 9
   <tr> <td>   <td> 2 <td>   <td>   <td> 9 <td>   <td>   <td> 1 <td>
   <tr> <td> 7 <td>   <td>   <td>   <td>   <td>   <td>   <td>   <td> 6
  <tbody>
   <tr> <td> 2 <td>   <td> 4 <td>   <td> 3 <td>   <td> 9 <td>   <td> 8
   <tr> <td>   <td>   <td>   <td>   <td>   <td>   <td>   <td>   <td>
   <tr> <td> 5 <td>   <td>   <td> 9 <td>   <td> 7 <td>   <td>   <td> 1
  <tbody>
   <tr> <td> 6 <td>   <td>   <td>   <td> 5 <td>   <td>   <td>   <td> 2
   <tr> <td>   <td>   <td>   <td>   <td> 7 <td>   <td>   <td>   <td>
   <tr> <td> 9 <td>   <td>   <td> 8 <td>   <td> 2 <td>   <td>   <td> 5
 </table>
</section>
描述表格的技术

对于表头不仅仅只在第一行及第一列的表格,以及一些通常情况下会使得阅读器难以理解其内容的表格,编码人员应该使用解释性信息来介绍该表格。这些信息对所有用户有效,但是对不能看到表格的用户尤为重要。例如屏幕阅读器的用户。

这些解释性信息应该包括对表格目的的介绍、单元格基本结构的大纲、突出显示趋势或图表以及指导用户如果使用表格。

举例说明,对于如下表格:

正负双方的特性
负面 特性 正面
悲伤 心情 高兴
不合格 成绩 合格

……一个解释表格布局的描述将有益于用户理解表格的内容,比如“特性在第二列中给出,该特性的负面信息在其左侧一列,正面信息在其右侧一列”。

下面是向表格中加入这些信息的各种方法:

使用独立的段落包围表格

<p>特性在第二列中给出,该特性的负面信息在其左侧一列,正面信息在其右侧一列。</p>
<table>
 <caption>正负双方的特性</caption>
 <thead>
  <tr>
   <th id="n">负面
   <th>特性
   <th>正面
 <tbody>
  <tr>
   <td headers="n r1">悲伤
   <th id="r1">心情
   <td>高兴
  <tr>
   <td headers="n r2">不合格
   <th id="r2">成绩
   <td>合格
</table>

使用表格的caption元素

<table>
 <caption>
  <strong>正负双方的特性</strong>
  <p>特性在第二列中给出,该特性的负面信息在其左侧一列,正面信息在其右侧一列。</p>
 </caption>
 <thead>
  <tr>
   <th id="n">负面
   <th>特性
   <th>正面
 <tbody>
  <tr>
   <td headers="n r1">悲伤
   <th id="r1">心情
   <td>高兴
  <tr>
   <td headers="n r2">不合格
   <th id="r2">成绩
   <td>合格
</table>

使用表格的caption元素下的details元素

<table>
 <caption>
  <strong>正负双方的特性</strong>
  <details>
   <summary>帮助</summary>
   <p>特性在第二列中给出,该特性的负面信息在其左侧一列,正面信息在其右侧一列。</p>
  </details>
 </caption>
 <thead>
  <tr>
   <th id="n">负面
   <th>特性
   <th>正面
 <tbody>
  <tr>
   <td headers="n r1">悲伤
   <th id="r1">心情
   <td>高兴
  <tr>
   <td headers="n r2">不合格
   <th id="r2">成绩
   <td>合格
</table>

使用表格外的figure元素

<figure>
 <figcaption>正负双方的特性</figcaption>
 <p>特性在第二列中给出,该特性的负面信息在其左侧一列,正面信息在其右侧一列。</p>
 <table>
  <thead>
   <tr>
    <th id="n">负面
    <th>特性
    <th>正面
  <tbody>
   <tr>
    <td headers="n r1">悲伤
    <th id="r1">心情
    <td>高兴
   <tr>
    <td headers="n r2">不合格
    <th id="r2">成绩
    <td>合格
 </table>
</figure>

使用表格外的figure元素下的figcaption元素

<figure>
 <figcaption>
  <strong>正负双方的特性</strong>
  <p>特性在第二列中给出,该特性的负面信息在其左侧一列,正面信息在其右侧一列。</p>
 </figcaption>
 <table>
  <thead>
   <tr>
    <th id="n">负面
    <th>特性
    <th>正面
  <tbody>
   <tr>
    <td headers="n r1">悲伤
    <th id="r1">心情
    <td>高兴
   <tr>
    <td headers="n r2">不合格
    <th id="r2">成绩
    <td>合格
 </table>
</figure>

编码人员还可以适当地使用其他的技术或上述技术的组合。

当然,调整表格结构,使表格清晰易懂,比撰写解释表格布局的描述更为有效。

对于上述例子中所使用的表格,可以通过下列方法对表格进行重新排列,将表头移动到表格的顶部及左侧,从而使表格不再需要解释以及headers属性。

<table>
 <caption>正负双方的特性</caption>
 <thead>
  <tr>
   <th>特性
   <th>负面
   <th>正面
 <tbody>
  <tr>
   <th>心情
   <td>悲伤
   <td>高兴
  <tr>
   <th>成绩
   <td>不合格
   <td>合格
</table>
布局表格的技术

良好的表格布局是一个表格提高其易读性和易用性的关键。

在视觉媒体中,提供行、列的边界以及交替使用行背景可以有效地提高复杂表格的易读性。

对于展示大量数字内容的表格,使用等宽字体有助于用户查看图表,特别是对于用户代理不渲染边界的情况。(不幸的是,由于历史原因,通常情况下默认不渲染表格的边界。)

在发声媒体中,表格中的单元格通过在阅读单元格的内容前表现相应的标题进行分割,同时允许用户通过网格方式浏览表格而不是按照源代码顺序序列化内容。

我们鼓励编码人员使用CSS实现这些效果。

我们鼓励用户代理在页面没有使用CSS以及表格不属于布局表格的情况下使用这些技术渲染表格。

caption元素

分类:

使用场景:
table元素的第一个子元素。
内容模型:
流式内容,但不能是table元素的后裔。
允许的属性:
全局属性
DOM接口:
interface HTMLTableCaptionElement : HTMLElement {};

如果caption元素拥有一个父元素且该元素是一个table元素,则caption元素表现作为其父table元素的标题。

caption元素是表格模型的一部分。

如果table元素是一个figure元素除figcaption外的唯一内容,caption元素应该被忽略并使用figcaption的值。

标题可以介绍表格的上下班,尽可能的是其容易被理解。

考虑如下表格:

1 2 3 4 5 6
1 2 3 4 5 6 7
2 3 4 5 6 7 8
3 4 5 6 7 8 9
4 5 6 7 8 9 10
5 6 7 8 9 10 11
6 7 8 9 10 11 12

在感官上这个表格很不清晰。可是,通过一个给出了表格编号(将在主要内容中提到)以及表格用途解释的标题,使表格容易被理解。

<caption>
<p>表一:
<p>该表展示了两次掷骰子后的总分。第一行表示第一次掷骰子所得的分数,第一列表示第二次掷骰子所得的分数。总分在两次掷骰子所得分数所对应的单元格中给出。
</caption>

colgroup元素

分类:

使用场景:
table元素子元素,在任意caption元素之后,且在任意theadtbodytfoottr元素之前。
内容模型:
如果存在span属性:空。
如果不存在span属性:零或多个col元素。
允许的属性:
全局属性
span
DOM接口:
interface HTMLTableColElement : HTMLElement { attribute unsigned long span; };

如果colgroup元素拥有一个父元素且该元素是一个table元素,则colgroup元素表现其父table元素的一一或多个

如果colgroup元素不包含col元素,则该元素可以标志一个span内容属性,该属性的值必须是一个大于零的有效的非负整数

colgroup元素以及该元素的span属性属于表格模型

span IDL属性必须反映同名的内容属性。其值必须被限制为仅为大于零的非负整数

col元素

分类:

使用场景:
没有span属性(原文链接至col元素的span属性,应为colgroup元素的span属性)colgroup元素子元素。
内容模型:
空。
允许的属性:
全局属性
span
DOM接口:
HTMLTableColElement,与colgroup元素相同。该接口定义了一个成员——span

如果col元素拥有一个父元素且该元素是一个colgroup元素,同时该colgroup元素拥有一个父元素且该元素是一个table元素,则col元素表现其父colgroup元素所表现的列组中的一或多个

该元素可以标志一个span内容属性,该属性的值必须是一个大于零的有效的非负整数

col元素以及该元素的span属性属于表格模型

span IDL属性必须反映同名的内容属性。其值必须被限制为仅为大于零的非负整数

tbody元素

分类:

使用场景:
table元素子元素,在任意captioncolgroupthead元素之后,但前提是table元素没有tr元素作为其子元素。
内容模型:
零或多个tr元素。
允许的属性:
全局属性
DOM接口:
interface HTMLTableSectionElement : HTMLElement { readonly attribute HTMLCollection rows; HTMLElement insertRow(in optional long index); void deleteRow(in long index); }; HTMLTableSectionElement接口同样用于theadtfoot元素。

如果tbody元素拥有一个父元素且该元素是一个table元素,则tbody元素表现由其父table元素中数据的主体部分组成的区块

tbody元素属于表格模型

此区块中不是标准描述,实现要求在下面给出。

tbody . rows

返回表格段落中tr元素的一个HTMLCollection

tr = tbody . insertRow( [ index ] )

创建一个tr元素,将其插入表格片段中由参数所指定的位置,并返回所创建的tr元素。

位置是相对于表格片段中的行的。参数被忽略时默认位置为-1。索引-1等价于在表格的结尾处插入元素。

如果给出的位置小于-1或大于表格片段行数,则抛出一个INDEX_SIZE_ERR异常。

tbody . deleteRow(index)

删除表格片段中指定位置的tr元素。

位置是相对于表格片段中的行的。索引-1等价于删除表格片段结尾处的元素。

如果给出的位置小于-1或大于表格行数,则抛出一个INDEX_SIZE_ERR异常。

rows属性必须返回一个以该元素为根的HTMLCollection,其过滤器只匹配该元素的tr子元素。

insertRow(index)方法在被一个table section(表格片段)元素调用时,必须执行下列操作:

  1. 如果index小于-1或大于rows集合中的元素数,则该方法必须抛出一个INDEX_SIZE_ERR异常。
  2. 如果index丢失、等于-1或等于rows集合中的元素数,则该方法必须创建一个tr元素,并将其追加到table section元素之中。
  3. 否则,该方法必须创建一个tr元素,将其作为table section元素的子元素插入到rows集合中的第indextr元素之前,并且最终必须返回新创建的tr元素。

deleteRow(index)方法必须从起父元素中删除rows集合中的第index个元素。如果index小于零或者大于等于rows集合中的元素数,该方法必须抛出一个INDEX_SIZE_ERR异常。

thead元素

分类:

使用场景:
table元素子元素,在任意captioncolgroup元素之后,且在任意tbodytfoottr元素之前,但前提是table元素没有其他thead元素作为其子元素。
内容模型:
零或多个tr元素。
允许的属性:
全局属性
DOM接口:
HTMLTableSectionElement,与tbody元素定义的接口相同。

如果thead元素拥有一个父元素且该元素是一个table元素,则thead元素表现由其父table元素中的列标签(表头)部分组成的区块

thead元素属于表格模型

下面的例子展示了如何使用thead元素。注意thead元素中thtd元素的使用:第一行为表头,第二行用于解释如何填充该表格。


<table>
 <caption>校园拍卖报名表</caption>
 <thead>
  <tr>
   <th><label for=e1>名称</label>
   <th><label for=e2>产品</label>
   <th><label for=e3>图片</label>
   <th><label for=e4>价格</label>
  <tr>
   <td>你的名字
   <td>你想要卖什么?
   <td>链接到一张图片
   <td>你的底价
 <tbody>
  <tr>
   <td>Ms Danus
   <td>甜甜圈
   <td><img src="http://example.com/mydoughnuts.png" title="Ms Danus的甜甜圈">
   <td>$45
  <tr>
   <td><input id=e1 type=text name=who required form=f>
   <td><input id=e2 type=text name=what required form=f>
   <td><input id=e3 type=url name=pic form=f>
   <td><input id=e4 type=number step=0.01 min=0 value=0 required form=f>
</table>
<form id=f action="/auction.cgi">
 <input type=button name=add value="Submit">
</form>

tfoot元素

分类:

使用场景:
table元素子元素,在任意captioncolgroupthead元素之后,且在任意tbodytr元素之前,但前提是table元素没有其他tfoot元素作为其子元素。
table元素子元素,在任意captioncolgrouptheadtbodytr元素之后,但前提是table元素没有其他tfoot元素作为其子元素。
内容模型:
零或多个tr元素。
允许的属性:
全局属性
DOM接口:
HTMLTableSectionElement,与tbody元素定义的接口相同。

如果thead元素拥有一个父元素且该元素是一个table元素,则tfoot元素表现由其父table元素中的总结(表尾)部分组成的区块

tfoot元素属于表格模型

tr元素

分类:

使用场景:
thead元素子元素。
tbody元素子元素。
tfoot元素子元素。
table元素子元素,在任意captioncolgroupthead元素之后,但前提是table元素没有tbody元素作为其子元素。
内容模型:
零或多个tdth元素。
允许的属性:
全局属性
DOM接口:
interface HTMLTableRowElement : HTMLElement { readonly attribute long rowIndex; readonly attribute long sectionRowIndex; readonly attribute HTMLCollection cells; HTMLElement insertCell(in optional long index); void deleteCell(in long index); };

tr元素表现table元素中单元格所组成的

tr元素属于表格模型

此区块中不是标准描述,实现要求在下面给出。

tr . rowIndex

返回当前行在表格的rows列表中的位置。

如果该元素不在表格之中,则返回-1。

tr . sectionRowIndex

返回当前行在表格片段的rows列表中的位置。

如果该元素不在表格段落中,则返回-1。

tr . cells

返回当前行中tdth元素的HTMLCollection

cell = tr . insertCell( [ index ] )

创建一个td元素,将其插入到表格中参数所所给出的位置,并返回该td

位置是相对于行中的单元格的。参数被忽略时默认位置为-1。索引-1等价于在行的结尾处插入元素。

如果给出的位置小于-1或大于行中的单元格数,则抛出一个INDEX_SIZE_ERR异常。

tr . deleteCell(index)

删除当前行中指定位置的tdth元素。

位置是相对于行中的单元格的。索引-1等价于删除行中的最后一个单元格。

如果给出的位置小于-1或大于行中的最后一个单元格的索引,或者行中没有单元格,则抛出一个INDEX_SIZE_ERR异常。

对于rowIndex属性,如果该元素拥有一个table元素作为其父元素,或者拥有一个tbodytheadtfoot元素作为其父元素且拥有一个table元素作为其祖父节点,则必须返回该tr元素在table元素的rows集合中的索引。如果没有上述的table元素,则该属性必须返回-1。

对于sectionRowIndex属性,如果该元素拥有一个tabletbodytheadtfoot元素作为其父元素,则必须返回该tr元素在其父元素的rows集合中的索引(对于表格,为HTMLTableElement.rows集合;对于表格片段,为HTMLTableRowElement.rows集合)。如果没有这样的父元素,则该属性必须返回-1。

对于cells属性,必须返回一个根为该tr元素的HTMLCollection,其过滤器仅匹配该tr元素的tdth子元素。

对于insertCell(index)方法,必须执行下列步骤:

  1. 如果index小于-1或大于cells集合中的元素数,则该方法必须抛出一个INDEX_SIZE_ERR异常。
  2. 如果index丢失、等于-1或等于cells集合中的元素数,则该方法必须创建一个td元素,并将其追加到tr元素之中,并返回新创建的td元素。
  3. 否则,该方法必须创建一个td元素,将其作为子元素插入到tr元素中cells集合的第indextdth元素之前,并且最终必须返回新创建的td元素。

对于deleteCell(index)方法,必须从其父元素中移除cells集合中的第index个元素。如果index小于零或大于等于cells集合中的元素数,则该方法必须抛出一个INDEX_SIZE_ERR异常。

td元素

分类:
区块根
使用场景:
tr元素的子元素。
内容模型:
流式内容
允许的属性:
全局属性
colspan
rowspan
headers
DOM接口:
interface HTMLTableDataCellElement : HTMLTableCellElement {};

tr元素表现表格中的数据单元格

td元素及其colspan#attr-tdth-rowspanheaders属性属于表格模型

th元素

分类:

使用场景:
tr元素的子元素。
内容模型:
段落式内容
允许的属性:
全局属性
colspan
rowspan
headers
DOM接口:
interface HTMLTableHeaderCellElement : HTMLTableCellElement { attribute DOMString scope; };

th元素表现表格中的表头单元格

th元素可以拥有一个scope内容属性。该scope属性是一个拥有五个状态的枚举属性,其中的四个拥有明确的关键词:

  • row关键词,映射到row状态
    row状态表示该表头单元格适用于同一行内其后跟随的单元格。
  • col关键词,映射到column状态
    column状态表示该表头单元格适用于同一列内其后跟随的单元格。
  • rowgroup关键词,映射到row group状态
    row group状态表示该表头单元格适用于同一行组内所有其余的单元格。如果tr元素不依靠行组,则该元素的scope属性禁止被设为row group状态。
  • colgroup关键词,映射到column group状态
    column group状态表示该表头单元格适用于同一列组内所有其余的单元格。如果tr元素不依靠列组,则该元素的scope属性禁止被设为column group状态。
  • auto状态
    auto状态使得表头单元格的适用范围根据上下文决定。

scope属性的'missing value default(缺省默认值)为auto状态。

th元素及其colspan#attr-tdth-rowspanheadersscope属性属于表格模型

scope IDL属性必须反映同名的内容属性,且其值被限制为已知的值

下面的例子展示了scope属性的rowgroup值是如何影响被一个表头单元格适配的数据单元格。

下面的标记片段展示了一个表格:

<table>
 <thead>
  <tr> <th> 编号 <th> 对象 <th> 平均值 <th> 最大值
 <tbody>
  <tr> <td> <th scope=rowgroup> 猫 <td> <td>
  <tr> <td> 93 <th scope=row> 腿 <td> 3.5 <td> 4
  <tr> <td> 10 <th scope=row> 尾巴 <td> 1 <td> 1
 <tbody>
  <tr> <td> <th scope=rowgroup> 讲英语的人 <td> <td>
  <tr> <td> 32 <th scope=row> 腿 <td> 2.67 <td> 4
  <tr> <td> 35 <th scope=row> 尾巴 <td> 0.33 <td> 1
</table>
编号 对象 平均值 最大值
93 3.5 4
10 尾巴 1 1
讲英语的人
32 2.67 4
35 尾巴 0.33 1

第一行中的表头向下方应有到各自所在列的所有行。

明确标记了scope属性的表头应用于其所在行组内出第一列以外的所有单元格。

其余的表头仅仅应用于其右侧的单元格。

table-scope-diagram.png

td和th元素的通用属性

tdth元素可以标记一个colspan内容属性,其值必须是一个大于零的有效的非负整数

tdth元素还可以标记一个rowspan内容属性,其值必须是一个有效的非负整数

这些属性给出的行数及列数表示该单元格的跨度。依照表格模型的描述,这些属性禁止被用于覆盖单元格。

tdth元素可以标记一个headers内容属性。如果标记了headers属性,则必须包含一个由大小写敏感无序的唯一的由空格分隔的标记的集合所组成的字符串,其中的每个标记必须是与该tdth元素(定义自表格模型)所在的同一个表格中的一个th元素的ID的值。

一个拥有IDidth元素,被认为是所有在同一个表格中的其headers属性的值包含一个IDid的标记的tdth元素的directly targeted(直接目标)。若存在一个th元素A及一个tdth元素B,不论AB直接目标,或者存在一个元素C,使得CB目标(原为为目标,不确定是否应为直接目标)AC直接目标,则元素A被认为是B目标

一个th元素禁止成为其自身的目标

colspanrowspanheaders属性属于表格模型

tdth元素实现了继承自HTMLTableCellElement接口的接口:

interface HTMLTableCellElement : HTMLElement {
           attribute unsigned long colSpan;
           attribute unsigned long rowSpan;
  [PutForwards=value] readonly attribute DOMSettableTokenList headers;
  readonly attribute long cellIndex;
};
此区块中不是标准描述,实现要求在下面给出。cell . cellIndex

返回单元格在其所在行的cells列表中的位置。由于之前的单元格可能会覆盖多行或多列,该属性的返回值不一定与单元格在表格中的横向位置相符。

如果该元素不在一个行内,则返回0。

colSpan IDL属性必须反映同名的内容属性。其值必须限制为只能为大于零的非负整数

rowSpan IDL属性必须反映同名的内容属性。如果按照非负整数解析该属性返回一个错误,则必须使用默认值“1”。

headers IDL属性必须反映同名的内容属性。

如果该元素拥有一个父tr元素,则cellIndex IDL属性必须返回该单元格的元素在其父元素的cells集合中的索引。如果没有这样的父元素,则该属性必须返回“0”。

处理模型

各种表格元素以及它们的内容属性一起定义了表格模型

一个表格由二维网格中按坐标(x, y)的槽位排列而得的单元格组成的。网格是有限的,为空或者拥有一或多个槽位。如果网格拥有一或多个槽位,则x坐标总是在范围0≤x<xwidth之中,并且y坐标总是在范围0≤y<yheight之中。如果xwidthyheight中的一个为零或两者均为零,则表格为空(没有槽位)。表格相当于table元素。

一个单元格是一组从槽位(cellx, celly)开始且拥有特定widthheight的槽位。使得单元格覆盖的所有坐标(x, y)的槽位满足cellxx<cellx+widthcellyy<celly+height。单元格可以是数据单元格也可以是标题单元格。数据单元格相当于td元素,标题单元格相当于th元素。两种单元格都可以拥有零或多个与其关联的标题单元格。

在某种错误的情况下,有可能出现两个单元格占据相同的槽位。

一个是一组完整的拥有特定的y值的从x=0到x=xwidth-1的槽位。行相当于tr元素。

一个是一组完整的拥有特定的x值的从y=0到y=yheight-1的槽位。列可以相当于col元素。在缺少col元素的情况下,列是隐含的。

一个行组是一组从槽位(0, groupy)开始且拥有特定height。使得行组覆盖的所有坐标(x, y)的槽位满足0≤x<xwidthgroupyy<groupy+height。行组相当于tbodytheadtfoot元素。并非所有的行都一定在一个行组之中。

一个列组是一组从槽位(groupx, 0)开始且拥有特定width。使得列组覆盖的所有坐标(x, y)的槽位满足groupxx<groupx+width且0≤y<yheight。列组相当于colgroup元素。并非所有的列都一定在一个列组之中。

行组不能互相覆盖。同样的,列组也不能互相覆盖。

一个单元格不能覆盖来自两个或更多行组的槽位。然而,一个单元格可以存在于多个列组之中。所有属于一个单元格一部分的槽位都属于零或一个行组以及零或多个列组

除了单元格行组以及列组之外,表格还拥有一个caption元素与其相关。它给出了表格的标题或说明。

一个表格模型错误是一个table元素及其后裔所展示的数据的错误。文档禁止包含表格模型错误。

创建一个表格

为了确定与一个table元素相关联的表格中的槽位与哪个元素相关联,确定表格的尺寸(xwidthyheight),以及确定是否存在表格模型错误,用户代理必须使用下列算法:

  1. xwidth为零。
  2. yheight为零。
  3. 挂起的tfoot元素tfoot元素的列表,初始化为空。
  4. 当前表格table元素所代表的表格xwidthyheight变量确定当前表格的尺寸。当前表格初始化为空。
  5. 如果table元素没有子元素,则返回当前表格(这将是空的),并停止这些步骤。
  6. table元素的第一个caption子元素与当前表格关联。如果没有这样的子元素,则表示没有与当前表格相关联的caption元素。
  7. 当前元素table元素的第一个子元素。
    如果本算法中的某一步需要将当前元素移动到表格的下一个子元素且没有这样的下一个子元素时,用户代理必须跳转至接近算法结尾的标为END的步骤。
  8. 当前元素不是下列元素之一,则移动当前元素table元素的下一个子元素:
  9. 如果当前元素是一个colgroup元素,执行下列子步骤:
    1. COLUMN GROUPS:按照下列恰当的情况处理当前元素
      • 如果当前元素拥有col元素作为子元素
        1. xstartxwidth的值。
        2. 当前列colgroup元素的第一个col子元素。
        3. COLUMNS:如果当前列col元素拥有一个span元素,则使用解析非负整数的规则解析该属性的值。
          如果处理该值的结果不是一个错误或零,则设跨度为该值。
          否则,如果col元素没有span属性,或试图解析该属性的值时返回了一个错误或者零,则设跨度为1。
        4. xwidth增加跨度数。
        5. 使当前表格中的跨度当前列col元素对应。
        6. 如果当前列不是colgroup元素的最后一个col子元素,则设当前列colgroup元素中的下一个col子元素,并且返回标有COLUMNS的步骤。
        7. 当前表格中一个新列组中x=xstart至x=xwidth的所有的,从槽位(xstart, 0)开始且拥有宽度xwidth-xstart,与colgroup元素对应。
      • 如果当前元素没有col元素作为子元素
        1. 如果colgroup元素拥有一个span属性,则使用解析非负整数的规则解析该属性的值。
          如果处理该值的结果不是一个错误或零,则设跨度为该值。
          否则,如果colgroup元素没有span属性,或试图解析该属性的值时返回了一个错误或者零,则设跨度为1。
        2. xwidth增加跨度数。
        3. 当前表格中一个新列组中的跨度,从槽位(xwidth-跨度, 0)开始且拥有宽度跨度,与colgroup元素对应。
    2. 移动当前元素table元素的下一个子元素。
    3. 当前元素不是下列元素之一,则移动当前元素table元素的下一个子元素:
    4. 如果当前元素是一个colgroup元素,则调转到前面标有COLUMN GROUPS的步骤。
  10. ycurrent为零。
  11. 向下发展的单元格列表为一个空列表。
  12. ROWS:若当前元素不是下列元素之一,则移动当前元素table元素的下一个子元素:
  13. 如果当前元素是一个tr元素,则执行处理行的算法移动当前元素table元素的下一个子元素,并且返回标有ROWS的步骤。
  14. 执行结束一个行组的算法
  15. 如果当前元素是一个tfoot元素,则将该元素添加至挂起的tfoot元素列表中,移动当前元素table元素的下一个子元素,并且返回标有ROWS的步骤。
  16. 如果当前元素是一个theadtbody元素,则执行处理行组的算法
  17. 移动当前元素table元素的下一个子元素。
  18. 返回标有ROWS的步骤。
  19. END:对于挂起的tfoot元素列表中的每一个tfoot元素,按照其树形结构顺序执行处理行组的算法
  20. 如果当前表格中存在一个只包含没有单元格与其对齐的槽位,则这是一个表格模型错误
  21. 返回当前表格

处理行组的算法,被上面的步骤所调用,用于处理theadtbodytfoot元素。

  1. ystartyheight的值。
  2. 对于正在处理的元素的每一个tr子元素,按照树形结构顺序执行处理行的算法
  3. 如果yheight>ystart,则将当前表格中一个新的行组中y=ystart至y=yheight-1的所有的,从槽位(0, ystart)开始且拥有宽度yheight-ystart,与被处理的元素对应。
  4. 执行结束一个行组的算法

结束一个行组的算法,被上面的步骤所调用,用于开始和结束一个行的区块。

  1. ycurrent小于yheight时,执行下列步骤:
    1. 执行向下发展单元格算法
    2. ycurrent增加1。
  2. 清空向下发展的单元格列表

处理行的算法,被上面的步骤所调用,用于处理tr元素。

  1. 如果yheight等于ycurrent,则将yheight增加1。(ycurrent用于不会大于 yheight。)
  2. xcurrent为0。
  3. 执行向下发展单元格算法
  4. 如果正在处理的tr元素没有tdth元素作为其子元素,则将ycurrent增加1,跳过这些步骤,并且返回上一个算法。
  5. 当前单元格为正在处理的tr元素的第一个tdth元素。
  6. CELLS:当xcurrent小于xwidth,且坐标(xcurrent, ycurrent)的槽位已经存在一个单元格与之相关时,将xcurrent增加1。
  7. 如果xcurrent等于xwidth,则将xwidth增加1。(xcurrent用于不会大于 xwidth。)
  8. 如果当前单元格拥有一个colspan属性,则处理该属性的值,并且设列跨度为处理结果。
    如果该值的处理失败、返回零或不存在该属性,则设列跨度为1。
  9. 如果当前单元格拥有一个rowspan属性,则处理该属性的值,并且设行跨度为处理结果。
    如果该值的处理失败或不存在该属性,则设行跨度为1。
  10. 如果行跨度为零,则设单元格向下发展为真,且设行跨度为1。否则,设单元格向下发展为假。
  11. 如果xwidth<xcurrent+列跨度,则设xwidthxcurrent+列跨度
  12. 如果yheight<ycurrent+行跨度,则设yheightycurrent+行跨度
  13. 使坐标(x, y),xcurrentx<xcurrent+列跨度ycurrenty<ycurrent+行跨度的槽位,被一个新的单元格c覆盖。这些槽位从(xcurrent, ycurrent)开始,且宽度为列跨度、高度为行跨度,它们与当前单元格元素相对应。
    如果当前单元格元素是一个th元素,设这个新单元格c为一个标题单元格;否则设其为数据单元格。
    为了设置当前单元格元素应用某个标题单元格,应使用下一节中描述的关联标题单元格算法
    如果涉及的槽位中的某些已经被一个单元格覆盖,则这是一个表格模型错误。这些槽位同时被两个单元格覆盖。
  14. 如果单元格向下发展为真,则将元组{c, xcurrent, 列跨度}加入向下发展的单元格列表之中。
  15. xcurrent增加列跨度数。
  16. 如果当前单元格是正在处理的tr元素的最后一个tdth子元素,则将ycurrent增加1,跳过这些步骤,并且返回上一个算法。
  17. 当前单元格为正在处理的tr元素中的下一个tdth子元素。
  18. 返回被标为CELLS的步骤。

如果上述算法需要用户代理执行向下发展单元格算法,用户代理必须,若存在,对于每个向下发展的单元格列表中的{单元格, 单元格x, 宽度}元组,扩展单元格单元格,使其同时覆盖坐标(x, ycurrent),单元格xx<单元格x+宽度的槽位。

创建数据单元格与标题单元格之间的关系

每个单元格可以关联零或多个标题单元格。对于主单元格单元格的关联标题单元格算法如下:

  1. 标题列表为一个空的单元格列表。
  2. 设(x, y)为主单元格关联的槽位的坐标。
    • 如果主单元格标记了一个headers属性
      1. 获取主单元格headers属性的值,并且将其以空格分隔,设ID列表为获得的标记的列表。
      2. 对于ID列表中的每个标记,如果Document中第一个ID等于该标记的元素是同一个表格中的单元格,且该单元格不是主单元格,则将其添加到标题列表之中。
    • 如果主单元格没有标记headers属性
      1. width主单元格的宽度。
      2. height主单元格的高度。
      3. 对于yy+height-1中的每一个y值,使用主单元格标题列表、初始坐标(x, y)以及增量Δx=-1和Δy=0,执行扫描并关联标题单元格的主要算法
      4. 对于xx+width-1中的每一个x值,使用主单元格标题列表、初始坐标(x, y)以及增量Δx=0和Δy=-1,执行扫描并关联标题单元格的主要算法
      5. 如果主单元格依靠一个行组,则向标题列表插入所有作为行组标题的、依靠同一个行组且x坐标小于或等于x+width-1且y坐标小于或等于y+height-1的单元格。
      6. 如果主单元格依靠一个列组,则向标题列表插入所有作为列组标题的、依靠同一个列组且x坐标小于或等于x+width-1且y坐标小于或等于y+height-1的单元格。
  3. 移除标题列表中的所有空单元格
  4. 移除标题列表中的任何重复。
  5. 如果存在,移除标题列表中的主单元格
  6. 标题列表中的标题关联到主单元格

使用主单元格标题列表、初始坐标(初始x, 初始y)以及增量Δx和Δy执行扫描并关联标题单元格的主要算法的过程如下:

  1. x等于初始x
  2. y等于初始y
  3. 不透明标题为一个空的单元格列表。
    • 如果主单元格是一个标题单元格
      • 在标题区块中为真,并且设当前标题区块中的标题为一个只包含主单元格的单元格列表。
    • 否则
      • 在标题区块中为假,并且设当前标题区块中的标题为一个空的单元格列表。
  4. LOOP:将x增加Δx;将y增加Δy
    注:对于每次对本算法的调用,Δx和Δy之一为-1,另一个为0。
  5. 如果xy小于0,则跳过本主要算法。
  6. 如果没有单元格覆盖槽位(x, y),或者有超过一个单元格覆盖槽位(x, y),则返回标有LOOP的步骤。
  7. 当前单元格为覆盖槽位(x, y)的单元格。
    • 如果当前单元格是一个标题单元格
      1. 在标题区块中为真。
      2. 当前单元格加入到当前标题区块中的标题之中。
      3. 阻塞为假。
        • 如果Δx为0
          • 如果存在不透明标题列表中的单元格依靠当前单元格所在的同一个x坐标且与当前单元格拥有相同宽度,则设阻塞为真。
            如果当前单元格不是一个列标题,则设阻塞为真。
        • 如果Δy为0
          • 如果存在不透明标题列表中的单元格依靠当前单元格所在的同一个y坐标且与当前单元格拥有相同高度,则设阻塞为真。
            如果当前单元格不是一个行标题,则设阻塞为真。
      4. 如果阻塞为假,则将当前单元格加入到标题列表之中。
    • 如果当前单元格是一个数据单元格且在标题区块中为真
      • 在标题区块中为假。将当前标题区块中的标题中的所有单元格加入到不透明标题列表之中,并且清空当前标题区块中的标题列表。
  8. 然会标有LOOP的步骤。

对于一个依靠坐标(x, y)槽位且拥有宽度width、高度height的单元格,如果下列条件为真,则该单元格被称为列标题

  • 单元格的scope属性处于状态,或
  • 单元格的scope属性处于自动状态,且覆盖y坐标y .. y+高度-1的槽位的单元格中没有数据单元格。

对于一个依靠坐标(x, y)槽位且拥有宽度width、高度height的单元格,如果下列条件为真,则该单元格被称为行标题

  • 单元格的scope属性处于状态,或
  • 单元格的scope属性处于自动状态,且单元格不是列标题,且覆盖x坐标x .. x+宽度-1的槽位的单元格中没有数据单元格。

如果一个标题单元格的scope属性处于列组状态,则该标题单元格被称为列组标题

如果一个标题单元格的scope属性处于行组状态,则该标题单元格被称为行组标题

如果一个单元格不包含任何元素,且如果该单元格拥有文本内容且其文本内容仅包含空白字符,则该单元格被称为空单元格

示例

本节不是标准描述

下面的例子展示了如何标记《Smithsonian physical tables》卷71,表45的下部。

<table>
 <caption>Specification values: <b>Steel</b>, <b>Castings</b>,
 Ann. A.S.T.M. A27-16, Class B;* P max. 0.06; S max. 0.05.</caption>
 <thead>
  <tr>
   <th rowspan=2>Grade.</th>
   <th rowspan=2>Yield Point.</th>
   <th colspan=2>Ultimate tensile strength</th>
   <th rowspan=2>Per cent elong. 50.8mm or 2 in.</th>
   <th rowspan=2>Per cent reduct. area.</th>
  </tr>
  <tr>
   <th>kg/mm<sup>2</sup></th>
   <th>lb/in<sup>2</sup></th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>Hard</td>
   <td>0.45 ultimate</td>
   <td>56.2</td>
   <td>80,000</td>
   <td>15</td>
   <td>20</td>
  </tr>
  <tr>
   <td>Medium</td>
   <td>0.45 ultimate</td>
   <td>49.2</td>
   <td>70,000</td>
   <td>18</td>
   <td>25</td>
  </tr>
  <tr>
   <td>Soft</td>
   <td>0.45 ultimate</td>
   <td>42.2</td>
   <td>60,000</td>
   <td>22</td>
   <td>30</td>
  </tr>
 </tbody>
</table>

该表格显示如下:

Specification values: Steel, Castings, Ann. A.S.T.M. A27-16, Class B;* P max. 0.06; S max. 0.05.
Grade. Yield Point. Ultimate tensile strength Per cent elong. 50.8mm or 2 in. Per cent reduct. area.
kg/mm2 lb/in2
Hard 0.45 ultimate 56.2 80,000 15 20
Medium 0.45 ultimate 49.2 70,000 18 25
Soft 0.45 ultimate 42.2 60,000 22 30

下面的例子展示了如何标记苹果公司2008财务年的10-K文件第46页中的毛利润表。

<table>
 <thead>
  <tr>
   <th>
   <th>2008
   <th>2007
   <th>2006
 <tbody>
  <tr>
   <th>Net sales
   <td>$ 32,479
   <td>$ 24,006
   <td>$ 19,315
  <tr>
   <th>Cost of sales
   <td>  21,334
   <td>  15,852
   <td>  13,717
 <tbody>
  <tr>
   <th>Gross margin
   <td>$ 11,145
   <td>$  8,154
   <td>$  5,598
 <tfoot>
  <tr>
   <th>Gross margin percentage
   <td>34.3%
   <td>34.0%
   <td>29.0%
</table>

下面的例子展示了如何标记该文档同一页下面的营业费用表:

<table>
 <colgroup> <col>
 <colgroup> <col> <col> <col>
 <thead>
  <tr> <th> <th>2008 <th>2007 <th>2006
 <tbody>
  <tr> <th scope=rowgroup> Research and development
       <td> $ 1,109 <td> $ 782 <td> $ 712
  <tr> <th scope=row> Percentage of net sales
       <td> 3.4% <td> 3.3% <td> 3.7%
 <tbody>
  <tr> <th scope=rowgroup> Selling, general, and administrative
       <td> $ 3,761 <td> $ 2,963 <td> $ 2,433
  <tr> <th scope=row> Percentage of net sales
       <td> 11.6% <td> 12.3% <td> 12.6%
</table>

该表格显示如下:

2008 2007 2006
Research and development $ 1,109 $ 782 $ 712
Percentage of net sales 3.4% 3.3% 3.7%
Selling, general, and administrative $ 3,761 $ 2,963 $ 2,433
Percentage of net sales 11.6% 12.3% 12.6%