HtmlTableAccessibility

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

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>