Skip to content

Commit 353b53f

Browse files
authored
fix: Use getAttribute(id) over .id (#374)
* fix implicit attribute us in duplicate-id * fix: Only access node id's through getAttribute * Minor build chores for whitespace and lockfile (#366) * chore: convert spaces to tabs in Gruntfile Align the file with the EditorConfig setting * chore: ignore package-lock.json This file is produced by NPM 5, but this repo didn't use the old shrinkwrap version so this should be excluded * fix: Set relatedNodes on color/link-in-block rules (#407) * chore: ignore growl in retire * Add instructions on debugging on CircleCI * chore: Fix spacing issues * fix implicit attribute us in duplicate-id * fix: Only access node id's through getAttribute * chore: Fix spacing issues * fix: JSHint issue
1 parent 5324040 commit 353b53f

File tree

15 files changed

+58
-36
lines changed

15 files changed

+58
-36
lines changed

lib/checks/aria/required-parent.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,12 @@ function getAriaOwners(element) {
3434
o = null;
3535

3636
while (element) {
37-
if (element.id) {
38-
o = document.querySelector('[aria-owns~=' + axe.commons.utils.escapeSelector(element.id) + ']');
37+
if (element.getAttribute('id')) {
38+
const id = axe.commons.utils.escapeSelector(element.getAttribute('id'));
39+
o = document.querySelector(`[aria-owns~=${id}]`);
3940
if (o) { owners.push(o); }
4041
}
41-
element = element.parentNode;
42+
element = element.parentElement;
4243
}
4344

4445
return owners.length ? owners : null;

lib/checks/label/explicit.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
if (node.id) {
2-
var label = document.querySelector('label[for="' + axe.commons.utils.escapeSelector(node.id) + '"]');
1+
if (node.getAttribute('id')) {
2+
const id = axe.commons.utils.escapeSelector(node.getAttribute('id'));
3+
const label = document.querySelector(`label[for="${id}"]`);
4+
35
if (label) {
46
return !!axe.commons.text.accessibleText(label);
57
}

lib/checks/label/multiple-label.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
var labels = [].slice.call(document.querySelectorAll('label[for="' +
2-
axe.commons.utils.escapeSelector(node.id) + '"]')),
3-
parent = node.parentNode;
1+
const id = axe.commons.utils.escapeSelector(node.getAttribute('id'));
2+
let labels = Array.from(document.querySelectorAll(`label[for="${id}"]`));
3+
let parent = node.parentNode;
44

55
if (labels.length) {
66
// filter out hidden labels because they're fine

lib/checks/shared/duplicate-id.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
// Since empty ID's are not meaningful and are ignored by Edge, we'll
22
// let those pass.
3-
if (!node.id.trim()) {
3+
if (!node.getAttribute('id').trim()) {
44
return true;
55
}
66

7-
var matchingNodes = document.querySelectorAll('[id="' + axe.commons.utils.escapeSelector(node.id) + '"]');
7+
const id = axe.commons.utils.escapeSelector(node.getAttribute('id'));
8+
var matchingNodes = document.querySelectorAll(`[id="${id}"]`);
89
var related = [];
10+
911
for (var i = 0; i < matchingNodes.length; i++) {
1012
if (matchingNodes[i] !== node) {
1113
related.push(matchingNodes[i]);

lib/checks/tables/td-headers-attr.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ for (var rowIndex = 0, rowLength = node.rows.length; rowIndex < rowLength; rowIn
88
}
99

1010
var ids = cells.reduce(function (ids, cell) {
11-
if (cell.id) {
12-
ids.push(cell.id);
11+
if (cell.getAttribute('id')) {
12+
ids.push(cell.getAttribute('id'));
1313
}
1414
return ids;
1515
}, []);
@@ -30,8 +30,8 @@ var badCells = cells.reduce(function (badCells, cell) {
3030

3131
if (headers.length !== 0) {
3232
// Check if the cell's id is in this list
33-
if (cell.id) {
34-
isSelf = (headers.indexOf(cell.id.trim()) !== -1);
33+
if (cell.getAttribute('id')) {
34+
isSelf = (headers.indexOf(cell.getAttribute('id').trim()) !== -1);
3535
}
3636

3737
// Check if the headers are of cells inside the table

lib/checks/tables/th-has-data-cells.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ var tableGrid = tableUtils.toGrid(node);
3030

3131
// Look for all the bad headers
3232
var out = headers.reduce(function (res, header) {
33-
if (header.id && reffedHeaders.indexOf(header.id) !== -1) {
33+
if (header.getAttribute('id') &&
34+
reffedHeaders.includes(header.getAttribute('id'))) {
3435
return (!res ? res : true);
3536
}
3637

lib/commons/table/is-header.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ table.isHeader = function (cell) {
1010
return true;
1111
}
1212

13-
if (cell.id) {
14-
return !!document.querySelector('[headers~="' + axe.utils.escapeSelector(cell.id) + '"]');
13+
if (cell.getAttribute('id')) {
14+
const id = axe.utils.escapeSelector(cell.getAttribute('id'));
15+
return !!document.querySelector(`[headers~="${id}"]`);
1516
}
1617

1718
return false;

lib/commons/text/accessible-text.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ var phrasingElements = ['A', 'EM', 'STRONG', 'SMALL', 'MARK', 'ABBR', 'DFN', 'I'
2121
*/
2222
function findLabel(element) {
2323
var ref = null;
24-
if (element.id) {
25-
ref = document.querySelector('label[for="' + axe.utils.escapeSelector(element.id) + '"]');
24+
if (element.getAttribute('id')) {
25+
const id = axe.utils.escapeSelector(element.getAttribute('id'));
26+
ref = document.querySelector(`label[for="${id}"]`);
2627
if (ref) {
2728
return ref;
2829
}

lib/commons/text/label.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ text.label = function (node) {
1414
}
1515

1616
// explicit label
17-
if (node.id) {
18-
ref = document.querySelector('label[for="' + axe.utils.escapeSelector(node.id) + '"]');
17+
if (node.getAttribute('id')) {
18+
const id = axe.commons.utils.escapeSelector(node.getAttribute('id'));
19+
ref = document.querySelector(`label[for="${id}"]`);
1920
candidate = ref && text.visible(ref, true);
2021
if (candidate) {
2122
return candidate;

lib/core/utils/get-selector.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ function getNthChildString (elm, selector) {
4949
const createSelector = {
5050
// Get ID properties
5151
getElmId (elm) {
52-
if (!elm.id) {
52+
if (!elm.getAttribute('id')) {
5353
return;
5454
}
55-
const id = '#' + escapeSelector(elm.id || '');
55+
const id = '#' + escapeSelector(elm.getAttribute('id') || '');
5656
if (
5757
// Don't include youtube's uid values, they change on reload
5858
!id.match(/player_uid_/) &&
@@ -88,7 +88,7 @@ const createSelector = {
8888
},
8989
// Has a name property, but no ID (Think input fields)
9090
getElmNameProp (elm) {
91-
if (!elm.id && elm.name) {
91+
if (!elm.hasAttribute('id') && elm.name) {
9292
return '[name="' + escapeSelector(elm.name) + '"]';
9393
}
9494
},

lib/core/utils/get-xpath.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,15 @@ function getXPathArray(node, path) {
4646
} while (sibling);
4747
}
4848

49-
if(node.nodeType === 1) {
49+
if (node.nodeType === 1) {
5050
var element = {};
5151
element.str = node.nodeName.toLowerCase();
5252
// add the id and the count so we can construct robust versions of the xpath
53-
if(node.getAttribute && node.getAttribute('id') &&
54-
node.ownerDocument.querySelectorAll('#' + axe.utils.escapeSelector(node.id)).length === 1) {
55-
53+
var id = node.getAttribute && axe.utils.escapeSelector(node.getAttribute('id'));
54+
if (id && node.ownerDocument.querySelectorAll('#' + id).length === 1) {
5655
element.id = node.getAttribute('id');
5756
}
58-
if(count > 1) {
57+
if (count > 1) {
5958
element.count = count;
6059
}
6160
path.push(element);

lib/rules/color-contrast-matches.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ if (nodeName === 'LABEL' || nodeParentLabel) {
5454
}
5555

5656
// label of disabled control associated w/ aria-labelledby
57-
if (node.id) {
58-
var candidate = doc.querySelector('[aria-labelledby~=' + axe.commons.utils.escapeSelector(node.id) + ']');
59-
if (candidate && candidate.disabled) {
57+
if (node.getAttribute('id')) {
58+
const id = axe.commons.utils.escapeSelector(node.getAttribute('id'));
59+
const candidate = doc.querySelector(`[aria-labelledby~="${id}"]`);
60+
if (candidate && candidate.hasAttribute('disabled')) {
6061
return false;
6162
}
6263
}

test/checks/shared/duplicate-id.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,13 @@ describe('duplicate-id', function () {
4949
assert.isTrue(checks['duplicate-id'].evaluate.call(checkContext, node));
5050
});
5151

52+
it('should allow overwrote ids', function () {
53+
fixture.innerHTML = '<form data-testelm="1" id="target"><label>mylabel' +
54+
'<input name="id">' +
55+
'</label></form>';
56+
var node = fixture.querySelector('[data-testelm="1"]');
57+
58+
assert.isTrue(checks['duplicate-id'].evaluate.call(checkContext, node));
59+
});
60+
5261
});

test/commons/table/is-header.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ describe('table.isHeader', function () {
55
}
66

77
var fixture = $id('fixture');
8+
var cell;
89

910
afterEach(function () {
10-
fixture.innerHTML = '';
11+
fixture.innerHTML = '<table><tr><th id="cell"></th></tr></table>';
12+
cell = $id('cell');
1113
});
1214

1315
it('should return true if table.isColumnHeader return true', function () {
@@ -19,7 +21,7 @@ describe('table.isHeader', function () {
1921
axe.commons.table.isRowHeader = function () {
2022
return false;
2123
};
22-
assert.isTrue(axe.commons.table.isHeader({}));
24+
assert.isTrue(axe.commons.table.isHeader(cell));
2325

2426
axe.commons.table.isColumnHeader = orig;
2527
axe.commons.table.isRowHeader = orig2;
@@ -34,7 +36,7 @@ describe('table.isHeader', function () {
3436
axe.commons.table.isColumnHeader = function () {
3537
return false;
3638
};
37-
assert.isTrue(axe.commons.table.isHeader({}));
39+
assert.isTrue(axe.commons.table.isHeader(cell));
3840

3941
axe.commons.table.isRowHeader = orig;
4042
axe.commons.table.isColumnHeader = orig2;
@@ -49,7 +51,7 @@ describe('table.isHeader', function () {
4951
axe.commons.table.isColumnHeader = function () {
5052
return false;
5153
};
52-
assert.isFalse(axe.commons.table.isHeader({}));
54+
assert.isFalse(axe.commons.table.isHeader(cell));
5355

5456
axe.commons.table.isRowHeader = orig;
5557
axe.commons.table.isColumnHeader = orig2;

test/core/utils/get-selector.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,11 @@ describe('axe.utils.getSelector', function () {
181181
var sel = axe.utils.getSelector({
182182
nodeName: 'a',
183183
classList: [],
184+
getAttribute: function () { },
184185
hasAttribute: function () { return false; },
185186
parentNode: {
186187
nodeName: 'b',
188+
getAttribute: function () { },
187189
hasAttribute: function () { return false; },
188190
classList: []
189191
}

0 commit comments

Comments
 (0)