diff --git a/lib/checks/navigation/region-evaluate.js b/lib/checks/navigation/region-evaluate.js
index 0e14b8a168..0e20b472cb 100644
--- a/lib/checks/navigation/region-evaluate.js
+++ b/lib/checks/navigation/region-evaluate.js
@@ -1,4 +1,5 @@
import * as dom from '../../commons/dom';
+import { hasChildTextNodes } from '../../commons/dom/has-content-virtual';
import { getRole } from '../../commons/aria';
import * as standards from '../../commons/standards';
import matches from '../../commons/matches';
@@ -45,7 +46,10 @@ function getRegionlessNodes(options) {
*/
function findRegionlessElms(virtualNode, options) {
const node = virtualNode.actualNode;
- // End recursion if the element is a landmark, skiplink, or hidden content
+ // End recursion if the element is...
+ // - a landmark
+ // - a skiplink
+ // - hidden content
if (
getRole(virtualNode) === 'button' ||
isRegion(virtualNode, options) ||
@@ -73,7 +77,8 @@ function findRegionlessElms(virtualNode, options) {
// @see https://github.com/dequelabs/axe-core/issues/2049
} else if (
node !== document.body &&
- dom.hasContent(node, /* noRecursion: */ true)
+ dom.hasContent(node, /* noRecursion: */ true) &&
+ !isShallowlyHidden(virtualNode)
) {
return [virtualNode];
@@ -86,6 +91,14 @@ function findRegionlessElms(virtualNode, options) {
}
}
+function isShallowlyHidden(virtualNode) {
+ // The element itself is not visible to screen readers, but its descendants might be
+ return (
+ ['none', 'presentation'].includes(getRole(virtualNode)) &&
+ !hasChildTextNodes(virtualNode)
+ );
+}
+
// Check if the current element is a landmark
function isRegion(virtualNode, options) {
const node = virtualNode.actualNode;
diff --git a/test/checks/navigation/region.js b/test/checks/navigation/region.js
index fb4d13b3e9..2620661d9d 100644
--- a/test/checks/navigation/region.js
+++ b/test/checks/navigation/region.js
@@ -46,14 +46,87 @@ describe('region', function () {
assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));
});
- it('should return false when img content is outside the region', function () {
- var checkArgs = checkSetup(
- '
Introduction
'
- );
+ it('should return false when img content is outside the region, no alt attribute at all', function () {
+ const checkArgs = checkSetup(`
+
+
Content
+ `);
+
+ assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));
+ });
+
+ it('should return true when img content outside of the region is decorative, via an empty alt attr', function () {
+ const checkArgs = checkSetup(`
+
+
Content
+ `);
+
+ assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));
+ });
+
+ it('should return true when img content outside of the region is explicitly decorative, via a presentation role', function () {
+ const checkArgs = checkSetup(`
+
+
Content
+ `);
+
+ assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));
+ });
+
+ it('should return false when img content outside of the region is focusable (implicit role=img)', function () {
+ const checkArgs = checkSetup(`
+
+
Content
+ `);
+
+ assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));
+ });
+
+ it('should return false when img content outside of the region has a global aria attribute (implicit role=img)', function () {
+ const checkArgs = checkSetup(`
+
+
+ `);
+
+ assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));
+ });
+
+ it('should return false when object has an aria-label', function () {
+ const checkArgs = checkSetup(`
+
+
Content
+ `);
assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));
});
+ it('should return false when a non-landmark has text content but a role=none', function () {
+ const checkArgs = checkSetup(`
+
apples
+
Content
+ `);
+
+ assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));
+ });
+
+ it('should return true when a non-landmark does NOT have text content and a role=none', function () {
+ const checkArgs = checkSetup(`
+
+
Content
+ `);
+
+ assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));
+ });
+
it('should return true when textless text content is outside the region', function () {
var checkArgs = checkSetup(
'
Introduction
'
@@ -166,6 +239,15 @@ describe('region', function () {
assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));
});
+ it('ignores native landmark elements with an overwriting role with a nested child', function () {
+ var checkArgs = checkSetup(`
+
Content
+
Content
+ `);
+
+ assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));
+ });
+
it('returns false for content outside of form tags with accessible names', function () {
var checkArgs = checkSetup(
'