123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102 |
- import isRowHeader from './is-row-header';
- import isColumnHeader from './is-column-header';
- import toGrid from './to-grid';
- import getCellPosition from './get-cell-position';
- import idrefs from '../dom/idrefs';
- import findUp from '../dom/find-up';
- /**
- * Loop through the table grid looking for headers and caching the result.
- * @param {String} headerType The type of header to look for ("row" or "col")
- * @param {Object} position The position of the cell to start looking
- * @param {Array} tablegrid A matrix of the table obtained using axe.commons.table.toGrid
- * @return {Array<HTMLTableCellElement>} Array of HTMLTableCellElements that are headers
- */
- function traverseForHeaders(headerType, position, tableGrid) {
- const property = headerType === 'row' ? '_rowHeaders' : '_colHeaders';
- const predicate = headerType === 'row' ? isRowHeader : isColumnHeader;
- const startCell = tableGrid[position.y][position.x];
- // adjust position by rowspan and colspan
- // subtract 1 from col/rowspan to make them 0 indexed
- const colspan = startCell.colSpan - 1;
- // ie11 returns 1 as the rowspan value even if it's set to 0
- const rowspanAttr = startCell.getAttribute('rowspan');
- const rowspanValue =
- parseInt(rowspanAttr) === 0 || startCell.rowspan === 0
- ? tableGrid.length
- : startCell.rowSpan;
- const rowspan = rowspanValue - 1;
- const rowStart = position.y + rowspan;
- const colStart = position.x + colspan;
- const rowEnd = headerType === 'row' ? position.y : 0;
- const colEnd = headerType === 'row' ? 0 : position.x;
- let headers;
- const cells = [];
- for (let row = rowStart; row >= rowEnd && !headers; row--) {
- for (let col = colStart; col >= colEnd; col--) {
- const cell = tableGrid[row] ? tableGrid[row][col] : undefined;
- if (!cell) {
- continue;
- }
- // stop traversing once we've found a cache
- const vNode = axe.utils.getNodeFromTree(cell);
- if (vNode[property]) {
- headers = vNode[property];
- break;
- }
- cells.push(cell);
- }
- }
- // need to check that the cells we've traversed are headers
- headers = (headers || []).concat(cells.filter(predicate));
- // cache results
- cells.forEach(tableCell => {
- const vNode = axe.utils.getNodeFromTree(tableCell);
- vNode[property] = headers;
- });
- return headers;
- }
- /**
- * Get any associated table headers for a `HTMLTableCellElement`
- * @method getHeaders
- * @memberof axe.commons.table
- * @instance
- * @param {HTMLTableCellElement} cell The cell of which to get headers
- * @param {Array} [tablegrid] A matrix of the table obtained using axe.commons.table.toGrid
- * @return {Array<HTMLTableCellElement>} Array of headers associated to the table cell
- */
- function getHeaders(cell, tableGrid) {
- if (cell.getAttribute('headers')) {
- const headers = idrefs(cell, 'headers');
- // testing has shown that if the headers attribute is incorrect the browser
- // will default to the table row/column headers
- if (headers.filter(header => header).length) {
- return headers;
- }
- }
- if (!tableGrid) {
- tableGrid = toGrid(findUp(cell, 'table'));
- }
- const position = getCellPosition(cell, tableGrid);
- // TODO: RTL text
- const rowHeaders = traverseForHeaders('row', position, tableGrid);
- const colHeaders = traverseForHeaders('col', position, tableGrid);
- return [].concat(rowHeaders, colHeaders).reverse();
- }
- export default getHeaders;
|