From 1610a8202b751b46e6b50b792d913e6066d771f2 Mon Sep 17 00:00:00 2001 From: Zack Goldberg Date: Fri, 15 Dec 2017 12:46:11 -0500 Subject: [PATCH] fix(commons/dom): change isFocusable to check visibility in screenreader mode fixes #647 --- lib/commons/dom/is-focusable.js | 2 +- test/commons/dom/is-natively-focusable.js | 79 ++++++++++++++++++++++- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/lib/commons/dom/is-focusable.js b/lib/commons/dom/is-focusable.js index 1a6273e5f2..b15fffc980 100644 --- a/lib/commons/dom/is-focusable.js +++ b/lib/commons/dom/is-focusable.js @@ -38,7 +38,7 @@ dom.isNativelyFocusable = function(el) { if (!el || el.disabled || - (!dom.isVisible(el) && el.nodeName.toUpperCase() !== 'AREA')) { + (!dom.isVisible(el, true) && el.nodeName.toUpperCase() !== 'AREA')) { return false; } diff --git a/test/commons/dom/is-natively-focusable.js b/test/commons/dom/is-natively-focusable.js index 6ba569baa7..3a69231492 100644 --- a/test/commons/dom/is-natively-focusable.js +++ b/test/commons/dom/is-natively-focusable.js @@ -3,6 +3,21 @@ describe('dom.isNativelyFocusable', function () { var fixture = document.getElementById('fixture'); + function hideByClipping (el) { + el.style.cssText = 'position: absolute !important;' + + ' clip: rect(0px 0px 0px 0px); /* IE6, IE7 */' + + ' clip: rect(0px, 0px, 0px, 0px);'; + } + + function hideByMovingOffScreen (el) { + el.style.cssText = 'position:absolute;' + + ' left:-10000px;' + + ' top:auto;' + + ' width:1px;' + + ' height:1px;' + + ' overflow:hidden;'; + } + afterEach(function () { document.getElementById('fixture').innerHTML = ''; }); @@ -72,14 +87,74 @@ describe('dom.isNativelyFocusable', function () { }); - it('should return false for non-visible elements', function () { - fixture.innerHTML = ''; + it('should return false for elements hidden with display:none', function () { + fixture.innerHTML = ''; 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 = ''; + var el = document.getElementById('target'); + + assert.isFalse(axe.commons.dom.isNativelyFocusable(el)); + + }); + + it('should return true for clipped elements', function () { + fixture.innerHTML = ''; + 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 = ''; + var el = document.getElementById('target'); + hideByMovingOffScreen(el); + + assert.isTrue(axe.commons.dom.isNativelyFocusable(el)); + + }); + + it('should return false for elements hidden with display:none on an ancestor', function () { + fixture.innerHTML = ''; + var el = document.getElementById('target'); + + assert.isFalse(axe.commons.dom.isNativelyFocusable(el)); + + }); + + it('should return false for elements hidden with visibility:hidden on an ancestor', function () { + fixture.innerHTML = ''; + 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 = '
'; + hideByClipping(document.getElementById('parent')); + var el = document.getElementById('target'); + + assert.isTrue(axe.commons.dom.isNativelyFocusable(el)); + + }); + + it('should return true for elements off-screened by an ancestor', function () { + fixture.innerHTML = '
'; + hideByMovingOffScreen(document.getElementById('parent')); + var el = document.getElementById('target'); + + assert.isTrue(axe.commons.dom.isNativelyFocusable(el)); + + }); + it('should return true for an anchor with an href', function () { fixture.innerHTML = ''; var el = document.getElementById('target');