Skip to content

Commit

Permalink
Added 3 new methods to HTMLCS.util to support presentation roles and …
Browse files Browse the repository at this point in the history
…aria-hidden attributes for #149 and #151.

isAccessibilityHidden() tests whether a given element is visible to an accessibility API.

isVisuallyHidden() was previously isHidden() and tests only if the element is visible in the browser.

getAllElements() replaces a private method in HTMLCS called _getAllTags(). The function accepts a parent element and returns an array of child elements that are not hidden from accessibility APIs. HTMLCS uses this as an initial gather of elements to test, but there are still many core query selectors used in individual sniffs which may need to be made aware of hidden elements and react accordingly.

Intially these seems to work with most common use case of avoiding tests of an element and all of it's children.
  • Loading branch information
ironikart committed Aug 24, 2016
1 parent 076aed7 commit cdba7a4
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 26 deletions.
2 changes: 1 addition & 1 deletion Auditor/HTMLCSAuditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -1815,7 +1815,7 @@ var HTMLCSAuditor = new function()
}

// Do not point to elem if its hidden. Use computed styles.
if (HTMLCS.util.isHidden(elem) === true) {
if (HTMLCS.util.isVisuallyHidden(elem) === true) {
return false;
}

Expand Down
72 changes: 70 additions & 2 deletions HTMLCS.Util.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ HTMLCS.util = function() {
};

/**
* Return true if an element is hidden.
* Return true if an element is hidden visually.
*
* If the computed style of an element cannot be determined for some reason,
* it is presumed it is NOT hidden.
Expand All @@ -181,7 +181,7 @@ HTMLCS.util = function() {
*
* @returns {Boolean}
*/
self.isHidden = function(element) {
self.isVisuallyHidden = function(element) {
var hidden = false;

// Do not point to elem if its hidden. Use computed styles.
Expand All @@ -203,6 +203,38 @@ HTMLCS.util = function() {
return hidden;
};


/**
* Returns true if the element is hidden from Accessibility APIs.
*
* @param {Node} element The element to check.
*
* @return {Boolean}
*/
self.isAccessibilityHidden = function(element) {

// WAI-ARIA presentation role.
if (element.hasAttribute('role') && element.getAttribute('role') === 'presentation') {
console.log(element);
return true;
}

// WAI-ARIA hidden attribute.
if (element.hasAttribute('aria-hidden') && element.getAttribute('aria-hidden') === 'true') {
return true;
}

// Accessibility APIs will ignore visibility: hidden and display: none.
var style = self.style(element);
if (style !== null) {
if (style.visibility === 'hidden' || style.display === 'none') {
return true;
}
}

return false;
};

/**
* Return true if an element is disabled.
*
Expand Down Expand Up @@ -247,6 +279,42 @@ HTMLCS.util = function() {
return true;
};

/**
* Returns all elements that are visible to the accessibility API.
*
* @param {Node} element The parent element to search.
*
* @return {Array}
*/
self.getAllElements = function(element) {
element = element || document;
var elements = Array.prototype.slice.call(element.getElementsByTagName('*'));
var hidden = elements.filter(function(elem) {
return HTMLCS.util.isAccessibilityHidden(elem) === true;
});

// We shouldn't be testing elements inside the injected auditor code if it's present.
var auditor = document.getElementById('HTMLCS-wrapper');
if (auditor) {
elements = elements.filter(function(elem) {
return auditor.contains(elem) === false;
});
}

return elements
.filter(function(elem) {
// Filter out direct matches to hidden elems.
return hidden.indexOf(elem) === -1;
})
.filter(function(elem) {
// Filter out children of hidden elements.
return hidden.filter(function(hiddenElem) {
console.log(hiddenElem, elem, hiddenElem.contains(elem));
return hiddenElem.contains(elem);
}).length ? false : true;
});
};

/**
* Returns true if the passed child is contained by the passed parent.
*
Expand Down
24 changes: 2 additions & 22 deletions HTMLCS.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ var HTMLCS = new function()
}
}

var elements = _getAllTags(element);
var elements = HTMLCS.util.getAllElements(element);
elements.unshift(element);
_run(elements, element, callback);
}
Expand Down Expand Up @@ -143,7 +143,7 @@ var HTMLCS = new function()

// Get all the elements in the parent element.
// Add the parent element too, which will trigger "_top" element codes.
var elements = _getAllTags(element);
var elements = HTMLCS.util.getAllElements(element);
elements.unshift(element);

// Run the sniffs.
Expand Down Expand Up @@ -574,24 +574,4 @@ var HTMLCS = new function()
document.getElementsByTagName('head')[0].appendChild(script);
}
};

/**
* Returns all the child elements in the given element.
*
* @param {Node|Document} [element=document] The parent element.
*
* @returns {Array} Array of Node objects.
*/
var _getAllTags = function(element) {
element = element || document;
var elements = element.getElementsByTagName('*');

// Convert to array. We can't use array features on a NodeList.
var elArray = [];
for (var i = 0; i < elements.length; i++) {
elArray.push(elements[i]);
}

return elArray;
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_4_1_4_3_Contrast = {
var node = toProcess.shift();

// This is an element.
if (node && (node.nodeType === 1) && (HTMLCS.util.isHidden(node) === false) && (HTMLCS.util.isDisabled(node) === false)) {
if (node && (node.nodeType === 1) && (HTMLCS.util.isVisuallyHidden(node) === false) && (HTMLCS.util.isDisabled(node) === false)) {
var processNode = false;
for (var i = 0; i < node.childNodes.length; i++) {
// Load up new nodes, but also only process this node when
Expand Down

0 comments on commit cdba7a4

Please sign in to comment.