axe-core
Version:
Accessibility engine for automated Web UI testing
153 lines (129 loc) • 4.5 kB
JavaScript
/* global table, dom */
/* eslint max-statements: ["error",70], complexity: ["error",47] */
/**
* 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
*/
table.isDataTable = function (node) {
var role = (node.getAttribute('role') || '').toLowerCase();
// The element is not focusable and has role=presentation
if ((role === 'presentation' || role === 'none') && !dom.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' || dom.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 (commons.aria.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 (dom.getElementCoordinates(node).width > dom.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;
};