Accessibility/TableHeaders: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
Line 73: Line 73:
</pre>
</pre>


=====Example2=====
=====Example 1=====
 
Examples exposes having row and column headers using row and column spans.
 
<pre>
<table>
  <tr>
    <th rowspan="2" colspan="2"></th>
    <th colspan="2">Animals</th>
  </tr>
  <tr>
    <th>Lion</th>
    <th>Tiger</th>
  </tr>
  <tr>
    <th rowspan="2">Vegetables</th>
    <th>Potato</th>
    <th>no</th>
    <th>no</th>
  </tr>
  <tr>
    <th>Carrot</th>
    <th>no</th>
    <th>no</th>
  </tr>
</table>
</pre>
 
The table visually is presented like
 
{| border="1" width="100%"
|-
| rowspan="2" colspan="2" |
| colspan="2" style="font-weight: bold" | Animals
|-
| style="font-weight: bold" | Lion
| style="font-weight: bold" | Tiger
|-
| rowspan="2" style="font-weight: bold" | Vegerables
| style="font-weight: bold" | Potato
| no
| no
|-
| style="font-weight: bold" | Carrot
| no
| no
|}
 
=====Example 3=====


It's more trick example than the previous one.
It's more trick example than the previous one.
Line 140: Line 188:
Let's consider few examples.
Let's consider few examples.


=====Example 1=====
Examples exposes having row and column headers using row and column spans.


<pre>
<table>
  <tr>
    <th rowspan="2" colspan="2"></th>
    <th colspan="2">Animals</th>
  </tr>
  <tr>
    <th>Lion</th>
    <th>Tiger</th>
  </tr>
  <tr>
    <th rowspan="2">Vegetables</th>
    <th>Potato</th>
    <th>no</th>
    <th>no</th>
  </tr>
  <tr>
    <th>Carrot</th>
    <th>no</th>
    <th>no</th>
  </tr>
</table>
</pre>
The table visually is presented like
{| border="1" width="100%"
|-
| rowspan="2" colspan="2" |
| colspan="2" style="font-weight: bold" | Animals
|-
| style="font-weight: bold" | Lion
| style="font-weight: bold" | Tiger
|-
| rowspan="2" style="font-weight: bold" | Vegerables
| style="font-weight: bold" | Potato
| no
| no
|-
| style="font-weight: bold" | Carrot
| no
| no
|}


=====Example 2=====
=====Example 4=====


This example uses thead and tfoot elements to introduce column headers. Note, tfoot duplicates information provided by thead. It makes sense for long tables.
This example uses thead and tfoot elements to introduce column headers. Note, tfoot duplicates information provided by thead. It makes sense for long tables.

Revision as of 05:47, 2 June 2009

Summary

This article is designed to cover implementation details of table headers in IAccessible2. As a matter of fact it is summary of discussion happen on IA2 mail list on this issue.

Terms

There is a difference in header term definition between IAccessible2 and markup tables. So that IA2 header is a table accessible (i.e. accessible implementing IAccessibleTable interface) containing cells accessibles describing the data cells or in other words rows or columns of the table. However, for example, ARIA header is cell element describing row or column of the table.

Here we will use the header term as an element containing header cells, where header cell is an element describing row or column of the table. This definition is close to IA2 terminology.

Tables used in Mozilla

Mozilla has couple of tables. These are ARIA grid/treegrid, HTML table and XUL tree and listbox.

ARIA grid/treegrid

ARIA has not markup for headers. However individual cells can be marked as header cells. ARIA provides role="rowheader" and role="columnheader" for this. ARIA implementation guide doesn't address how to find header cells for the given data cell and visa versa. We can use algorithm for HTML tables as the frist approaching. Attribute aria-describedby can be used similarly with @headers attribute on HTML tables.

HTML table

Natively HTML table has markup to provide column header only. The html:thead and html:tfoot elements are used for this. The thead and tfoot elements can contain several rows (html:tr) that contains individual cells (html:th or html:td).

However html:th can be used outside of html:thead or html:tfoot elements and be used to provide heading information as well. HTML 4 specification defines algorithm of finding heading information.

Also HTML specification defines @scope and @headers attributes on cells of the table. Attribute @scope is used to specify the kind of header cell, i.e. this cell is row or column header cell. Attribute @header used to link the data cell with header cells directly.

XUL tree and listbox

XUL tree and listbox has markup to provide column header only similar to HTML tables. XUL treecols and treecol elements of XUL tree serves for this. XUL listhead and listheaders elements are used for XUL listbox. XUL treecols and listhead elements are column headers, treecol and listheader elements are header cells.

Examples

Let's consider few examples.

Example 1

It's simple example that exposes row and column headers the same time.

Fruits John Ivan
Apples 10 12
Oranges 1 9

Here "Fruits", "John" and "Ivan" cells are column header cells, "Apples", "Oranges" are row header cells.

The following ARIA markup can be used to create the table like this.

<div role="grid">
  <div role="row">
    <span role="columnheader">Fruits</span>
    <span role="columnheader">John</span>
    <span rolw="columnheader">Ivan</span>
  </div>
  <div role="row">
    <span role="rowheader">Apples</span>
    <span role="gridcell">10</span>
    <span rolw="gridcel">12</span>
  </div>
  <div role="row">
    <span role="rowheader">Oranges</span>
    <span role="gridcell">1</span>
    <span rolw="gridcell">9</span>
  </div>
</div>
Example 1

Examples exposes having row and column headers using row and column spans.

<table>
  <tr>
    <th rowspan="2" colspan="2"></th>
    <th colspan="2">Animals</th>
  </tr>
  <tr>
    <th>Lion</th>
    <th>Tiger</th>
  </tr>
  <tr>
    <th rowspan="2">Vegetables</th>
    <th>Potato</th>
    <th>no</th>
    <th>no</th>
  </tr>
  <tr>
    <th>Carrot</th>
    <th>no</th>
    <th>no</th>
  </tr>
</table>

The table visually is presented like

Animals
Lion Tiger
Vegerables Potato no no
Carrot no no
Example 3

It's more trick example than the previous one.

<div role="grid">
  <div role="row">
    <span role="columnheader">Products</span>
    <span role="columnheader">Downloads number per year</span>
  </div>
  <div role="row">
    <span role="rowheader"></span>
    <span role="columnheader">2008</span>
  </div>
  <div role="row">
    <span role="rowheader">Product #1</span>
    <span role="gridcell">1 billion</span>
  </div>
  <div role="row">
    <span role="rowheader">Product #2</span>
    <span role="gridcell">1 billion</span>
  </div>
  <div role="row">
    <span role="rowheader"></span>
    <span role="columnheader">2009</span>
  </div>
  <div role="row">
    <span role="rowheader">Product #1</span>
    <span role="gridcell">2 billions</span>
  </div>
  <div role="row">
    <span role="rowheader">Product #2</span>
    <span role="gridcell">2 billions</span>
  </div>
</div>

Visually this table might be presented like

Products Downloads number per year
2008
Product #1 1 billion
Product #2 1 billion
2009
Product #1 2 billions
Product #2 2 billions

You could argue this is not well turned usage of tables and here two tables should be used. However it' might be worth to consider it.

Let's consider few examples.


Example 4

This example uses thead and tfoot elements to introduce column headers. Note, tfoot duplicates information provided by thead. It makes sense for long tables.

  <table>
    <thead>
      <tr>
        <th>Month</th>
        <th>Employee</th>
        <th>Quantity</th>
      </tr>
    </thead>
    <tfoot>
      <tr>
        <th>Month</th>
        <th>Employee</th>
        <th>Quantity</th>
      </tr>
    </tfoot>
    <tbody>
     <tr>
        <th>January</th>
        <th>John Nobody</th>
        <th>40</th>
      </tr>
     <tr>
        <th>February</th>
        <th>Lily Worker</th>
        <th>12</th>
     </tr>
     <tr>
        <th>March</th>
        <th>John Nobody</th>
        <th>30</th>
      </tr>
   </tbody>
  </table>

This table visually is presented like

Month Employee Quantity
January John Nobody 40
February Lily Worker 12
March John Nobody 30
Month Employee Quantity
Example 3

This example uses thead and tfoot elements to introduce column headers. Here's tfoot brings kind of summary of the table and contains data cell and rowheader cell.

  <table border="1">
    <thead>
      <tr>
        <th>Month</th>
        <th>Employee</th>
        <th>Quantity</th>
      </tr>
   </thead>
    <tfoot>
      <th colspan="2">Total</th>
      <th>82</th>
    </tfoot>
   <tbody>
     <tr>
        <th>January</th>
        <th>John Nobody</th>
        <th>40</th>
      </tr>
     <tr>
        <th>February</th>
        <th>Lily Worker</th>
        <th>12</th>
     </tr>
     <tr>
        <th>March</th>
        <th>John Nobody</th>
        <th>30</th>
      </tr>
   </tbody>
  </table>

This table visually is presented like

Month Employee Quantity
January John Nobody 40
February Lily Worker 12
March John Nobody 30
Total 82

Ways to expose table headers in IA2

There are two ways to provide information about table headers to AT. These ways have both pluses and minuses.

Header Tables

The first way is to implement rowHeader and columnHeader of IAccessibleTable interface.

HRESULT rowHeader(
  [out] IAccessibleTable **accessibleTable,
  [out, retval] long * startingColumnIndex) [get]

HRESULT columnHeader(
  [out] IAccessibleTable **accessibleTable,
  [out, retval] long * startingRowIndex) [get]

Relations

IAccessible2 provides relations attribute to get all exposed relations on this accessible.

HRESULT IAccessible2::relations(
  [in] long maxRelations,
  [out, size_is(maxRelations), length_is(*nRelations)] IAccessibleRelation ** relations,
  [out, retval] long * nRelations)	 [get]

The second way is to implement IA2_RELATION_DESCRIBED_BY and IA2_RELATION_DESCRIPTION_FOR accessible relations which link header cells with data cells. So that header cell accessibles expose DESCRIPTION_FOR relation with multiple targets pointing to data cells and data cell accessible expose DESCRIPTION_BY relation with multiple targets pointing to header cells. Once you acquired IAccessibleRelation object for the interested accessible you can use targets method to get all accessible targets.

HRESULT IAccessibleRelation::targets(
  [in] long maxTargets,
  [out, size_is(maxTargets), length_is(*nTargets)] IUnknown ** targets,
  [out, retval] long * nTargets ) [get]

Problem

XUL trees and listboxes only have a persistent way to provide header from markup. Persistent way means tree or listbox elements have a table header if and only if header element is provided in markup. HTML tables haven't unique way to provide column header information. Moreover there is no markup for HTML table to provide row header, however html:th elements can be used to create row header visually. And ARIA hasn't markup for headers at all. But AT wants to deal with all tables in unique way.

Proposal

Table header hierarchy

Expose a virtual tree for headers, i.e. this tree is not linked with main accessible tree and root of the tree is accessible table for header. This tree should have usual structure for accessible tables.

Column header table (the case of one row):

table
  row
    header_cell header_cell

Row header table (the case of column):

table
  row
    header_cell
  row
    header_cell

Cell and row elements might be exposed twice as accessible objects (one accessible object is for main accessible tree, another one is virtual header tree). However these twin accessible objects are different. For example, this makes IAccessible navigation methods to work independently, so that if you run through parents of cell from virtual tree then you will achieve header table, if you run up from cell of main tree then you will achieve original table accessible. As well, "table-cell-index" returns index in header table for virtual cell and returns index in main table for normal cell.

If states are changed for the one accessible then states are changed for its twin accessible in another subtree. For example, if you call IAccessibleTable::selectColumn on virtual header table then it selects cells of primary table as well and visa versa.

Events and HitTest

Since virtual table header is not linked with main tree then it's impossible to find accessibles of virtual tree by HitTest. As well this is the reason AccessibleObjectFromEvent might not work. So no events should be expected from table header accessible tree. AT can listen original table accessibles on mutation event to requery table header accessible.