Skip to content

Commit

Permalink
fix: introduce dom.isHiddenWithCSS for use in dom.isFocusable (#1211)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeeyyy authored Nov 8, 2018
1 parent 3c86c0e commit 2cff417
Show file tree
Hide file tree
Showing 4 changed files with 389 additions and 1 deletion.
2 changes: 1 addition & 1 deletion lib/commons/dom/is-focusable.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
function focusDisabled(el) {
return (
el.disabled ||
(!dom.isVisible(el, true) && el.nodeName.toUpperCase() !== 'AREA')
(dom.isHiddenWithCSS(el) && el.nodeName.toUpperCase() !== 'AREA')
);
}

Expand Down
60 changes: 60 additions & 0 deletions lib/commons/dom/is-hidden-with-css.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* eslint complexity: ["error", 13], max-statements: ["error", 25] */
/* global dom */

/**
* Determine whether an element is hidden based on css
* @method isHiddenWithCSS
* @memberof axe.commons.dom
* @instance
* @param {HTMLElement} el The HTML Element
* @param {Boolean} descendentVisibilityValue (Optional) immediate descendant visibility value used for recursive computation
* @return {Boolean} the element's hidden status
*/
dom.isHiddenWithCSS = function isHiddenWithCSS(el, descendentVisibilityValue) {
if (el.nodeType === 9) {
// 9 === Node.DOCUMENT
return false;
}

if (el.nodeType === 11) {
// 11 === Node.DOCUMENT_FRAGMENT_NODE
el = el.host; // swap to host node
}

if (['STYLE', 'SCRIPT'].includes(el.nodeName.toUpperCase())) {
return false;
}

const style = window.getComputedStyle(el, null);
if (!style) {
throw new Error('Style does not exist for the given element.');
}

const displayValue = style.getPropertyValue('display');
if (displayValue === 'none') {
return true;
}

const HIDDEN_VISIBILITY_VALUES = ['hidden', 'collapse'];
const visibilityValue = style.getPropertyValue('visibility');
if (
HIDDEN_VISIBILITY_VALUES.includes(visibilityValue) &&
!descendentVisibilityValue
) {
return true;
}

if (
HIDDEN_VISIBILITY_VALUES.includes(visibilityValue) &&
(descendentVisibilityValue &&
HIDDEN_VISIBILITY_VALUES.includes(descendentVisibilityValue))
) {
return true;
}

const parent = dom.getComposedParent(el);
if (parent && !HIDDEN_VISIBILITY_VALUES.includes(visibilityValue)) {
return dom.isHiddenWithCSS(parent, visibilityValue);
}
return false;
};
16 changes: 16 additions & 0 deletions test/commons/dom/is-focusable.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,14 @@ describe('is-focusable', function() {
assert.isFalse(axe.commons.dom.isNativelyFocusable(el));
});

it('should return false for elements collapsed with visibility:collapse', function() {
fixture.innerHTML =
'<button id="target" style="visibility: collapse">button</button>';
var el = document.getElementById('target');

assert.isFalse(axe.commons.dom.isNativelyFocusable(el));
});

it('should return true for clipped elements', function() {
fixture.innerHTML = '<button id="target">button</button>';
var el = document.getElementById('target');
Expand Down Expand Up @@ -262,6 +270,14 @@ describe('is-focusable', function() {
assert.isFalse(axe.commons.dom.isNativelyFocusable(el));
});

it('should return false for elements collapsed with visibility:collapse on an ancestor', function() {
fixture.innerHTML =
'<div id="parent" style="visibility: collapse"><button id="target">button</button></div>';
var el = document.getElementById('target');

assert.isFalse(axe.commons.dom.isNativelyFocusable(el));
});

it('should return true for elements with a clipped ancestor', function() {
fixture.innerHTML =
'<div id="parent"><button id="target">button</button></div>';
Expand Down
Loading

0 comments on commit 2cff417

Please sign in to comment.