Skip to content

Commit

Permalink
fix(commons/dom): change isFocusable to use unfocusableBecauseHidden
Browse files Browse the repository at this point in the history
method instead of isVisible

Adds functionality for detecting when hidden elements are still
focusable or in focus order.

fixes dequelabs#647
  • Loading branch information
0ddfell0w committed Dec 15, 2017
1 parent 9e3ca45 commit 60fe2c8
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 2 deletions.
16 changes: 15 additions & 1 deletion lib/commons/dom/is-focusable.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
/* global dom */
/* jshint maxcomplexity: 20 */

/**
* Determines if an element is hidden in such a way that makes it unfocusable
* @private
* @param {Element} el
* @return {Boolean} Whether the element is unfocusable due to being hidden
*/
function unfocusableBecauseHidden (el) {
var style = window.getComputedStyle(el);
return (
style == null || style.getPropertyValue('visibility') === 'hidden' ||
style.getPropertyValue('display') === 'none');
}

/**
* Determines if an element is focusable
* @method isFocusable
Expand Down Expand Up @@ -38,7 +52,7 @@ dom.isNativelyFocusable = function(el) {

if (!el ||
el.disabled ||
(!dom.isVisible(el) && el.nodeName.toUpperCase() !== 'AREA')) {
(unfocusableBecauseHidden(el) && el.nodeName.toUpperCase() !== 'AREA')) {
return false;
}

Expand Down
45 changes: 44 additions & 1 deletion test/commons/dom/is-natively-focusable.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,21 @@ describe('dom.isNativelyFocusable', function () {

var fixture = document.getElementById('fixture');

function hideByClipping (el) {
el.style = 'position: absolute !important;' +
' clip: rect(1px 1px 1px 1px); /* IE6, IE7 */' +
' clip: rect(1px, 1px, 1px, 1px);';
};

function hideByMovingOffScreen (el) {
el.style = 'position:absolute;' +
' left:-10000px;' +
' top:auto;' +
' width:1px;' +
' height:1px;' +
' overflow:hidden;';
};

afterEach(function () {
document.getElementById('fixture').innerHTML = '';
});
Expand Down Expand Up @@ -72,14 +87,40 @@ describe('dom.isNativelyFocusable', function () {

});

it('should return false for non-visible elements', function () {
it('should return false for elements hidden with display:none', function () {
fixture.innerHTML = '<input type="text" id="target" style="display: none">';
var el = document.getElementById('target');

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

});

it('should return false for elements hidden with visibility:hidden', function () {
fixture.innerHTML = '<input type="text" id="target" style="visibility: hidden">';
var el = document.getElementById('target');

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

});

it('should return true for clipped elements', function () {
fixture.innerHTML = '<input type="text" id="target">';
var el = document.getElementById('target');
hideByClipping(el);

assert.isTrue(axe.commons.dom.isNativelyFocusable(el));

});

it('should return true for elements positioned off screen', function () {
fixture.innerHTML = '<input type="text" id="target">';
var el = document.getElementById('target');
hideByMovingOffScreen(el);

assert.isTrue(axe.commons.dom.isNativelyFocusable(el));

});

it('should return true for an anchor with an href', function () {
fixture.innerHTML = '<a href="something.html" id="target"></a>';
var el = document.getElementById('target');
Expand Down Expand Up @@ -135,4 +176,6 @@ describe('dom.isNativelyFocusable', function () {
assert.isFalse(axe.commons.dom.isNativelyFocusable(el));

});


});

0 comments on commit 60fe2c8

Please sign in to comment.