Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit b3cb84d

Browse files
devversionThomasBurleson
authored andcommitted
fix(aria): $mdAria should not use texts from aria-hidden nodes
* $mdAria is currently just using `textContent` for retrieving the `aria-label`. This is not valid, since some child-texts can be hidden in aria (`aria-hidden="true") * Using a TreeWalker is super elegant and performant. The current use of the TreeWalker is supported by all browser we support. Fixes #7376 Closes #7957
1 parent 1fc2939 commit b3cb84d

File tree

2 files changed

+57
-6
lines changed

2 files changed

+57
-6
lines changed

src/core/services/aria/aria.js

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ function AriaService($$rAF, $log, $window, $interpolate) {
5050

5151
function expectWithText(element, attrName) {
5252
var content = getText(element) || "";
53-
var hasBinding = content.indexOf($interpolate.startSymbol())>-1;
53+
var hasBinding = content.indexOf($interpolate.startSymbol()) > -1;
5454

5555
if ( hasBinding ) {
5656
expectAsync(element, attrName, function() {
@@ -62,7 +62,26 @@ function AriaService($$rAF, $log, $window, $interpolate) {
6262
}
6363

6464
function getText(element) {
65-
return (element.text() || "").trim();
65+
element = element[0] || element;
66+
var walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false);
67+
var text = '';
68+
69+
var node;
70+
while (node = walker.nextNode()) {
71+
if (!isAriaHiddenNode(node)) {
72+
text += node.textContent;
73+
}
74+
}
75+
76+
return text.trim() || '';
77+
78+
function isAriaHiddenNode(node) {
79+
while (node.parentNode && (node = node.parentNode) !== element) {
80+
if (node.getAttribute && node.getAttribute('aria-hidden') === 'true') {
81+
return true;
82+
}
83+
}
84+
}
6685
}
6786

6887
function childHasAttribute(node, attrName) {
@@ -74,17 +93,18 @@ function AriaService($$rAF, $log, $window, $interpolate) {
7493
return (style.display === 'none');
7594
}
7695

77-
if(hasChildren) {
96+
if (hasChildren) {
7897
var children = node.childNodes;
79-
for(var i=0; i<children.length; i++){
98+
for (var i=0; i < children.length; i++) {
8099
var child = children[i];
81-
if(child.nodeType === 1 && child.hasAttribute(attrName)) {
82-
if(!isHidden(child)){
100+
if (child.nodeType === 1 && child.hasAttribute(attrName)) {
101+
if (!isHidden(child)) {
83102
hasAttr = true;
84103
}
85104
}
86105
}
87106
}
107+
88108
return hasAttr;
89109
}
90110
}

src/core/services/aria/aria.spec.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,37 @@ describe('$mdAria service', function() {
6060
button.remove();
6161

6262
}));
63+
64+
it('should correctly retrieve the aria-label text', inject(function($compile, $rootScope, $mdAria) {
65+
var container = $compile(
66+
'<div>' +
67+
'PLAIN' +
68+
'<span>SPAN</span>' +
69+
'<div>DIV</div>' +
70+
'</div>'
71+
)($rootScope);
72+
73+
$mdAria.expectWithText(container, 'aria-label');
74+
75+
expect(container[0].textContent).toBe('PLAINSPANDIV');
76+
expect(container.attr('aria-label')).toBe('PLAINSPANDIV');
77+
}));
78+
79+
it('should ignore aria-hidden texts when retrieving aria-label', inject(function($compile, $rootScope, $mdAria) {
80+
var container = $compile(
81+
'<div>' +
82+
'PLAIN' +
83+
'<span aria-hidden="true">SPAN</span>' +
84+
'<div aria-hidden="true">DIV</div>' +
85+
'</div>'
86+
)($rootScope);
87+
88+
$mdAria.expectWithText(container, 'aria-label');
89+
90+
expect(container[0].textContent).toBe('PLAINSPANDIV');
91+
expect(container.attr('aria-label')).toBe('PLAIN');
92+
}));
93+
6394
});
6495

6596
});

0 commit comments

Comments
 (0)