123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- 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;
|