Skip to content

HtmlTableAccessibility

Thomas Schraitle edited this page Mar 18, 2018 · 2 revisions

The following XSL customization inserts the headers attribute in HTML table cells. It only supports header rows, not row headers in one of the columns.

Sample DocBook XML

<informaltable tabstyle="content">
<tgroup cols="4">
<colspec colwidth="630*" colname="col1" />
<colspec colwidth="2970*" colname="col2" />
<colspec colwidth="1350*" colname="col3" />
<colspec colwidth="3240*" colname="col4" />
<thead>
<row>
<entry colname="col1">
<para role="atgtablebody">A</para>
</entry>
<entry colname="col2">
<para role="atgtablebody">Quick Brown Fox Jumps</para>
</entry>
<entry colname="col3">
<para role="atgtablebody">Over</para>
</entry>
<entry colname="col4">
<para role="atgtablebody">The Lazy Dog</para>
</entry>
</row>
</thead>
<tbody>
<row>
<entry colname="col1">
<para role="atgtablebody">B</para>
</entry>
<entry colname="col2">
<para role="atgtablebody">How high?</para>
</entry>
<entry colname="col3">
<para role="atgtablebody">Under</para>
</entry>
<entry colname="col4">
<para role="atgtablebody">The long day</para>
</entry>
</row>
<row>
<entry colname="col1">
<para role="atgtablebody">C</para>
</entry>
<entry colname="col2">
<para role="atgtablebody">Why?</para>
</entry>
<entry colname="col3">
<para role="atgtablebody">Through</para>
</entry>
<entry colname="col4">
<para role="atgtablebody">The leering dentist</para>
</entry>
</row>
</tbody>
</tgroup>
</informaltable>

Customized DocBook XSL

<xsl:template match="d:entry|d:entrytbl" name="entry">

[snip]

<xsl:if test="$charoff != ''">
<xsl:attribute name="charoff">
<xsl:value-of select="$charoff"/>
</xsl:attribute>
</xsl:if>


<xsl:choose>
<xsl:when test="$cellgi = 'th'">
<xsl:attribute name="id">
<xsl:text>colhead</xsl:text>
<xsl:value-of select="generate-id()"/>
</xsl:attribute>
</xsl:when>
<xsl:when test="$cellgi = 'td'">
<xsl:if test="ancestor::*[@tabstyle='content']">
<xsl:attribute name="headers">
<xsl:choose>
<xsl:when test="@colname">
<xsl:call-template name="findHeaderCell">
<xsl:with-param name="colname" select="@colname" />
</xsl:call-template>
</xsl:when>
<xsl:when test="@nameend">
<xsl:call-template name="findMultipleHeaderCells">
<xsl:with-param name="namest" select="@namest" />
<xsl:with-param name="nameend" select="@nameend" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:message>ERROR: attempting to add a headers attribute to a table cell that has no colname or nameend attribute.</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:message>ERROR: The cell type <xsl:value-of select="$cellgi" /> is neither th nor td.</xsl:message>
</xsl:otherwise>
</xsl:choose>

[snip]

</xsl:template>

<xsl:template name="findHeaderCell">
<xsl:param name="colname" />
<xsl:choose>
<xsl:when test="../../../d:thead/d:row/d:entry[@colname=$colname]">
<xsl:text>colhead</xsl:text>
<xsl:value-of select="generate-id(../../../d:thead/d:row/d:entry[@colname=$colname])"/>
</xsl:when>
<xsl:when test="../../../d:thead/d:row/d:entry[@namest=$colname]">
<xsl:text>colhead</xsl:text>
<xsl:value-of select="generate-id(../../../d:thead/d:row/d:entry[@namest=$colname])"/>
</xsl:when>
<xsl:when test="../../../d:colspec[@colname=$colname]">
<xsl:apply-templates select="../../../d:colspec[@colname=$colname]" mode="findColspecWithMatch" />
</xsl:when>
<xsl:otherwise>
<xsl:message>ERROR: No table header cells nor colspecs match the current table body cell. My value is: <xsl:value-of select="."/></xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template name="findMultipleHeaderCells">
<xsl:param name="namest" />
<xsl:param name="nameend" />
<xsl:param name="currentColname" select="$nameend" />
<xsl:param name="previousMatches" />
<!-- Find the header cell for the current column. Write its value. -->
<xsl:variable name="newValue">
<xsl:call-template name="findHeaderCell">
<xsl:with-param name="colname" select="$currentColname" />
</xsl:call-template>
<xsl:text> </xsl:text>
<xsl:value-of select="$previousMatches" />
</xsl:variable>

<!-- If the currentColname does not match the namest, find the colspec element
preceding the one that has a colname matching the currentColname. Call this
template with its colname in the currentColname param. -->

<xsl:choose>
<xsl:when test="$currentColname != $namest">
<xsl:call-template name="findMultipleHeaderCells">
<xsl:with-param name="currentColname">
<xsl:apply-templates select="../../../d:colspec[@colname=$currentColname]" mode="findPrecedingColname" />
</xsl:with-param>
<xsl:with-param name="namest" select="$namest" />
<xsl:with-param name="nameend" select="$nameend" />
<xsl:with-param name="previousMatches" select="$newValue" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$newValue" />
</xsl:otherwise>
</xsl:choose>

</xsl:template>

Sample HTML Output

<table border="1">
<colgroup>
<col width="7%" class="col1" />
<col width="37%" class="col2" />
<col width="16%" class="col3" />
<col width="40%" class="col4" />
</colgroup>
<thead>
<tr>
<th id="colheadd0e171">
<p class="atgtablebody">A</p>
</th>
<th id="colheadd0e174">
<p class="atgtablebody">Quick Brown Fox Jumps</p>
</th>
<th id="colheadd0e177">
<p class="atgtablebody">Over</p>
</th>
<th id="colheadd0e180">
<p class="atgtablebody">The Lazy Dog</p>
</th>
</tr>
</thead>
<tbody>
<tr>
<td headers="colheadd0e171">
<p class="atgtablebody">B</p>
</td>
<td headers="colheadd0e174">
<p class="atgtablebody">How high?</p>
</td>
<td headers="colheadd0e177">
<p class="atgtablebody">Under</p>
</td>
<td headers="colheadd0e180">
<p class="atgtablebody">The long day</p>
</td>
</tr>
<tr>
<td headers="colheadd0e171">
<p class="atgtablebody">C</p>
</td>
<td headers="colheadd0e174">
<p class="atgtablebody">Why?</p>
</td>
<td headers="colheadd0e177">
<p class="atgtablebody">Through</p>
</td>
<td headers="colheadd0e180">
<p class="atgtablebody">The leering dentist</p>
</td>
</tr>
</tbody>
</table>
Clone this wiki locally