axe-core
Version:
Accessibility engine for automated Web UI testing
196 lines (174 loc) • 5.12 kB
JavaScript
import getRoleType from '../aria/get-role-type';
import isFocusable from '../dom/is-focusable';
import findUp from '../dom/find-up';
import getElementCoordinates from '../dom/get-element-coordinates';
import getViewportSize from '../dom/get-viewport-size';
/**
* Determines whether a table is a data table
* @method isDataTable
* @memberof axe.commons.table
* @instance
* @param {HTMLTableElement} node The table to test
* @return {Boolean}
* @see http://asurkov.blogspot.co.uk/2011/10/data-vs-layout-table.html
*/
function isDataTable(node) {
var role = (node.getAttribute('role') || '').toLowerCase();
// The element is not focusable and has role=presentation
if ((role === 'presentation' || role === 'none') && !isFocusable(node)) {
return false;
}
// Table inside editable area is data table always since the table structure is crucial for table editing
if (
node.getAttribute('contenteditable') === 'true' ||
findUp(node, '[contenteditable="true"]')
) {
return true;
}
// Table having ARIA table related role is data table
if (role === 'grid' || role === 'treegrid' || role === 'table') {
return true;
}
// Table having ARIA landmark role is data table
if (getRoleType(role) === 'landmark') {
return true;
}
// Table having datatable="0" attribute is layout table
if (node.getAttribute('datatable') === '0') {
return false;
}
// Table having summary attribute is data table
if (node.getAttribute('summary')) {
return true;
}
// Table having legitimate data table structures is data table
if (node.tHead || node.tFoot || node.caption) {
return true;
}
// colgroup / col - colgroup is magically generated
for (
var childIndex = 0, childLength = node.children.length;
childIndex < childLength;
childIndex++
) {
if (node.children[childIndex].nodeName.toUpperCase() === 'COLGROUP') {
return true;
}
}
var cells = 0;
var rowLength = node.rows.length;
var row, cell;
var hasBorder = false;
for (var rowIndex = 0; rowIndex < rowLength; rowIndex++) {
row = node.rows[rowIndex];
for (
var cellIndex = 0, cellLength = row.cells.length;
cellIndex < cellLength;
cellIndex++
) {
cell = row.cells[cellIndex];
if (cell.nodeName.toUpperCase() === 'TH') {
return true;
}
if (
!hasBorder &&
(cell.offsetWidth !== cell.clientWidth ||
cell.offsetHeight !== cell.clientHeight)
) {
hasBorder = true;
}
if (
cell.getAttribute('scope') ||
cell.getAttribute('headers') ||
cell.getAttribute('abbr')
) {
return true;
}
if (
['columnheader', 'rowheader'].includes(
(cell.getAttribute('role') || '').toLowerCase()
)
) {
return true;
}
// abbr element as a single child element of table cell
if (
cell.children.length === 1 &&
cell.children[0].nodeName.toUpperCase() === 'ABBR'
) {
return true;
}
cells++;
}
}
// Table having nested table is layout table
if (node.getElementsByTagName('table').length) {
return false;
}
// Table having only one row or column is layout table (row)
if (rowLength < 2) {
return false;
}
// Table having only one row or column is layout table (column)
var sampleRow = node.rows[Math.ceil(rowLength / 2)];
if (sampleRow.cells.length === 1 && sampleRow.cells[0].colSpan === 1) {
return false;
}
// Table having many columns (>= 5) is data table
if (sampleRow.cells.length >= 5) {
return true;
}
// Table having borders around cells is data table
if (hasBorder) {
return true;
}
// Table having differently colored rows is data table
var bgColor, bgImage;
for (rowIndex = 0; rowIndex < rowLength; rowIndex++) {
row = node.rows[rowIndex];
if (
bgColor &&
bgColor !==
window.getComputedStyle(row).getPropertyValue('background-color')
) {
return true;
} else {
bgColor = window
.getComputedStyle(row)
.getPropertyValue('background-color');
}
if (
bgImage &&
bgImage !==
window.getComputedStyle(row).getPropertyValue('background-image')
) {
return true;
} else {
bgImage = window
.getComputedStyle(row)
.getPropertyValue('background-image');
}
}
// Table having many rows (>= 20) is data table
if (rowLength >= 20) {
return true;
}
// Wide table (more than 95% of the document width) is layout table
if (
getElementCoordinates(node).width >
getViewportSize(window).width * 0.95
) {
return false;
}
// Table having small amount of cells (<= 10) is layout table
if (cells < 10) {
return false;
}
// Table containing embed, object, applet of iframe elements (typical advertisements elements) is layout table
if (node.querySelector('object, embed, iframe, applet')) {
return false;
}
// Otherwise it's data table
return true;
}
export default isDataTable;