aria-api 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aria.js +117 -47
- package/lib/attrs.js +1 -1
- package/lib/constants.js +61 -10
- package/lib/name.js +55 -36
- package/package.json +1 -1
package/dist/aria.js
CHANGED
|
@@ -188,7 +188,7 @@ const getAttribute = function(el, key) {
|
|
|
188
188
|
return true;
|
|
189
189
|
}
|
|
190
190
|
const style = window.getComputedStyle(el);
|
|
191
|
-
if (style.display === 'none' || style.visibility === 'hidden') {
|
|
191
|
+
if (style.display === 'none' || style.visibility === 'hidden' || style.visibility === 'collapse') {
|
|
192
192
|
return true;
|
|
193
193
|
}
|
|
194
194
|
}
|
|
@@ -257,14 +257,18 @@ exports.attributes = {
|
|
|
257
257
|
'activedescendant': 'id',
|
|
258
258
|
'atomic': 'bool',
|
|
259
259
|
'autocomplete': 'token',
|
|
260
|
+
'braillelabel': 'string',
|
|
261
|
+
'brailleroledescription': 'string',
|
|
260
262
|
'busy': 'bool',
|
|
261
263
|
'checked': 'tristate',
|
|
262
264
|
'colcount': 'int',
|
|
263
265
|
'colindex': 'int',
|
|
266
|
+
'colindextext': 'string',
|
|
264
267
|
'colspan': 'int',
|
|
265
268
|
'controls': 'id-list',
|
|
266
269
|
'current': 'token',
|
|
267
270
|
'describedby': 'id-list',
|
|
271
|
+
'description': 'string',
|
|
268
272
|
'details': 'id',
|
|
269
273
|
'disabled': 'bool',
|
|
270
274
|
'dropeffect': 'token-list',
|
|
@@ -294,6 +298,7 @@ exports.attributes = {
|
|
|
294
298
|
'roledescription': 'string',
|
|
295
299
|
'rowcount': 'int',
|
|
296
300
|
'rowindex': 'int',
|
|
301
|
+
'rowindextext': 'string',
|
|
297
302
|
'rowspan': 'int',
|
|
298
303
|
'selected': 'bool-undefined',
|
|
299
304
|
'setsize': 'int',
|
|
@@ -323,6 +328,19 @@ exports.attributeWeakMapping = {
|
|
|
323
328
|
// https://www.w3.org/TR/html/dom.html#sectioning-content-2
|
|
324
329
|
const scoped = ['article *', 'aside *', 'nav *', 'section *'].join(',');
|
|
325
330
|
|
|
331
|
+
const svgSelectors = function(selector) {
|
|
332
|
+
return [
|
|
333
|
+
// `${selector}:has(> title:not(:empty))`,
|
|
334
|
+
// `${selector}:has(> desc:not(:empty))`,
|
|
335
|
+
`${selector}[aria-label]`,
|
|
336
|
+
`${selector}[aria-roledescription]`,
|
|
337
|
+
`${selector}[aria-labelledby]`,
|
|
338
|
+
`${selector}[aria-describedby]`,
|
|
339
|
+
`${selector}[tabindex]`,
|
|
340
|
+
`${selector}[role]`,
|
|
341
|
+
];
|
|
342
|
+
};
|
|
343
|
+
|
|
326
344
|
// https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings
|
|
327
345
|
// https://www.w3.org/TR/wai-aria/roles
|
|
328
346
|
exports.roles = {
|
|
@@ -337,6 +355,7 @@ exports.roles = {
|
|
|
337
355
|
application: {},
|
|
338
356
|
article: {
|
|
339
357
|
selectors: ['article'],
|
|
358
|
+
childRoles: ['comment'],
|
|
340
359
|
},
|
|
341
360
|
banner: {
|
|
342
361
|
selectors: [`header:not(main *, ${scoped})`],
|
|
@@ -399,6 +418,9 @@ exports.roles = {
|
|
|
399
418
|
abstract: true,
|
|
400
419
|
childRoles: ['button', 'link', 'menuitem'],
|
|
401
420
|
},
|
|
421
|
+
comment: {
|
|
422
|
+
nameFromContents: true,
|
|
423
|
+
},
|
|
402
424
|
complementary: {
|
|
403
425
|
selectors: [
|
|
404
426
|
`aside:not(${scoped})`,
|
|
@@ -494,8 +516,8 @@ exports.roles = {
|
|
|
494
516
|
},
|
|
495
517
|
generic: {
|
|
496
518
|
selectors: [
|
|
497
|
-
'a:not([href])',
|
|
498
|
-
'area:not([href])',
|
|
519
|
+
'a:not([*|href])',
|
|
520
|
+
'area:not([*|href])',
|
|
499
521
|
`aside:not(${scoped}):not([aria-label]):not([aria-labelledby]):not([title])`,
|
|
500
522
|
'b',
|
|
501
523
|
'bdi',
|
|
@@ -519,8 +541,23 @@ exports.roles = {
|
|
|
519
541
|
'graphics-document': {
|
|
520
542
|
selectors: ['svg'],
|
|
521
543
|
},
|
|
522
|
-
'graphics-object': {
|
|
523
|
-
|
|
544
|
+
'graphics-object': {
|
|
545
|
+
selectors: [
|
|
546
|
+
...svgSelectors('symbol'),
|
|
547
|
+
...svgSelectors('use'),
|
|
548
|
+
],
|
|
549
|
+
},
|
|
550
|
+
'graphics-symbol': {
|
|
551
|
+
selectors: [
|
|
552
|
+
...svgSelectors('circle'),
|
|
553
|
+
...svgSelectors('ellipse'),
|
|
554
|
+
...svgSelectors('line'),
|
|
555
|
+
...svgSelectors('path'),
|
|
556
|
+
...svgSelectors('polygon'),
|
|
557
|
+
...svgSelectors('polyline'),
|
|
558
|
+
...svgSelectors('rect'),
|
|
559
|
+
],
|
|
560
|
+
},
|
|
524
561
|
grid: {
|
|
525
562
|
childRoles: ['treegrid'],
|
|
526
563
|
},
|
|
@@ -535,7 +572,11 @@ exports.roles = {
|
|
|
535
572
|
'fieldset',
|
|
536
573
|
'hgroup',
|
|
537
574
|
'optgroup',
|
|
575
|
+
...svgSelectors('foreignObject'),
|
|
576
|
+
...svgSelectors('g'),
|
|
538
577
|
'text',
|
|
578
|
+
...svgSelectors('textPath'),
|
|
579
|
+
...svgSelectors('tspan'),
|
|
539
580
|
],
|
|
540
581
|
childRoles: ['row', 'select', 'toolbar', 'graphics-object'],
|
|
541
582
|
},
|
|
@@ -546,8 +587,13 @@ exports.roles = {
|
|
|
546
587
|
'level': 2,
|
|
547
588
|
},
|
|
548
589
|
},
|
|
549
|
-
|
|
550
|
-
selectors: [
|
|
590
|
+
image: {
|
|
591
|
+
selectors: [
|
|
592
|
+
'img:not([alt=""])',
|
|
593
|
+
'graphics-symbol',
|
|
594
|
+
...svgSelectors('image'),
|
|
595
|
+
...svgSelectors('mesh'),
|
|
596
|
+
],
|
|
551
597
|
childRoles: ['doc-cover'],
|
|
552
598
|
},
|
|
553
599
|
input: {
|
|
@@ -595,7 +641,7 @@ exports.roles = {
|
|
|
595
641
|
],
|
|
596
642
|
},
|
|
597
643
|
link: {
|
|
598
|
-
selectors: ['a[href]', 'area[href]'],
|
|
644
|
+
selectors: ['a[*|href]', 'area[href]'],
|
|
599
645
|
childRoles: ['doc-backlink', 'doc-biblioref', 'doc-glossref', 'doc-noteref'],
|
|
600
646
|
nameFromContents: true,
|
|
601
647
|
},
|
|
@@ -625,6 +671,9 @@ exports.roles = {
|
|
|
625
671
|
main: {
|
|
626
672
|
selectors: ['main'],
|
|
627
673
|
},
|
|
674
|
+
mark: {
|
|
675
|
+
selectors: ['mark'],
|
|
676
|
+
},
|
|
628
677
|
marquee: {},
|
|
629
678
|
math: {
|
|
630
679
|
selectors: ['math'],
|
|
@@ -648,11 +697,10 @@ exports.roles = {
|
|
|
648
697
|
},
|
|
649
698
|
},
|
|
650
699
|
menuitem: {
|
|
651
|
-
childRoles: ['menuitemcheckbox'],
|
|
700
|
+
childRoles: ['menuitemcheckbox', 'menuitemradio'],
|
|
652
701
|
nameFromContents: true,
|
|
653
702
|
},
|
|
654
703
|
menuitemcheckbox: {
|
|
655
|
-
childRoles: ['menuitemradio'],
|
|
656
704
|
nameFromContents: true,
|
|
657
705
|
defaults: {
|
|
658
706
|
'checked': 'false',
|
|
@@ -759,12 +807,13 @@ exports.roles = {
|
|
|
759
807
|
'emphasis',
|
|
760
808
|
'figure',
|
|
761
809
|
'group',
|
|
762
|
-
'
|
|
810
|
+
'image',
|
|
763
811
|
'insertion',
|
|
764
812
|
'landmark',
|
|
765
813
|
'list',
|
|
766
814
|
'listitem',
|
|
767
815
|
'log',
|
|
816
|
+
'mark',
|
|
768
817
|
'marquee',
|
|
769
818
|
'math',
|
|
770
819
|
'note',
|
|
@@ -772,6 +821,7 @@ exports.roles = {
|
|
|
772
821
|
'status',
|
|
773
822
|
'strong',
|
|
774
823
|
'subscript',
|
|
824
|
+
'suggestion',
|
|
775
825
|
'superscript',
|
|
776
826
|
'table',
|
|
777
827
|
'tabpanel',
|
|
@@ -966,6 +1016,7 @@ for (const role in exports.roles) {
|
|
|
966
1016
|
exports.aliases = {
|
|
967
1017
|
'presentation': 'none',
|
|
968
1018
|
'directory': 'list',
|
|
1019
|
+
'img': 'image',
|
|
969
1020
|
};
|
|
970
1021
|
|
|
971
1022
|
exports.nameFromDescendant = {
|
|
@@ -985,25 +1036,43 @@ const constants = require('./constants.js');
|
|
|
985
1036
|
const atree = require('./atree.js');
|
|
986
1037
|
const query = require('./query.js');
|
|
987
1038
|
|
|
988
|
-
const
|
|
989
|
-
|
|
990
|
-
const
|
|
991
|
-
const inline = styles.display
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1039
|
+
const addSpaces = function(text, el, pseudoSelector) {
|
|
1040
|
+
// https://github.com/w3c/accname/issues/3
|
|
1041
|
+
const styles = window.getComputedStyle(el, pseudoSelector);
|
|
1042
|
+
const inline = styles.display === 'inline';
|
|
1043
|
+
return inline ? text : ` ${text} `;
|
|
1044
|
+
};
|
|
1045
|
+
|
|
1046
|
+
const getPseudoContent = function(el, pseudoSelector) {
|
|
1047
|
+
const styles = window.getComputedStyle(el, pseudoSelector);
|
|
1048
|
+
let tail = styles.getPropertyValue('content').trim();
|
|
1049
|
+
let ret = [];
|
|
1050
|
+
|
|
1051
|
+
let match;
|
|
1052
|
+
while (tail.length) {
|
|
1053
|
+
if (match = tail.match(/^"([^"]*)"/)) {
|
|
1054
|
+
ret.push(match[1]);
|
|
1055
|
+
} else if (match = tail.match(/^([a-z-]+)\(([^)]*)\)/)) {
|
|
1056
|
+
if (match[1] === 'attr') {
|
|
1057
|
+
ret.push(el.getAttribute(match[2]) || '');
|
|
1058
|
+
}
|
|
1059
|
+
} else if (match = tail.match(/^([a-z-]+)/)) {
|
|
1060
|
+
if (match[1] === 'open-quote' || match[1] === 'close-quote') {
|
|
1061
|
+
ret.push('"');
|
|
1062
|
+
}
|
|
1063
|
+
} else if (match = tail.match(/^\//)) {
|
|
1064
|
+
ret = [];
|
|
1000
1065
|
} else {
|
|
1001
|
-
|
|
1066
|
+
// invalid content, ignore
|
|
1067
|
+
return '';
|
|
1002
1068
|
}
|
|
1069
|
+
tail = tail.slice(match[0].length).trim();
|
|
1003
1070
|
}
|
|
1071
|
+
|
|
1072
|
+
return addSpaces(ret.join(''), el, pseudoSelector);
|
|
1004
1073
|
};
|
|
1005
1074
|
|
|
1006
|
-
const getContent = function(root, visited) {
|
|
1075
|
+
const getContent = function(root, ongoingLabelledBy, visited) {
|
|
1007
1076
|
const children = atree.getChildNodes(root);
|
|
1008
1077
|
|
|
1009
1078
|
let ret = '';
|
|
@@ -1014,12 +1083,8 @@ const getContent = function(root, visited) {
|
|
|
1014
1083
|
} else if (node.nodeType === node.ELEMENT_NODE) {
|
|
1015
1084
|
if (node.tagName.toLowerCase() === 'br') {
|
|
1016
1085
|
ret += '\n';
|
|
1017
|
-
} else if (window.getComputedStyle(node).display.substr(0, 6) === 'inline' &&
|
|
1018
|
-
node.tagName.toLowerCase() !== 'input' &&
|
|
1019
|
-
node.tagName.toLowerCase() !== 'img') { // https://github.com/w3c/accname/issues/3
|
|
1020
|
-
ret += getName(node, true, visited);
|
|
1021
1086
|
} else {
|
|
1022
|
-
ret +=
|
|
1087
|
+
ret += getName(node, true, ongoingLabelledBy, visited);
|
|
1023
1088
|
}
|
|
1024
1089
|
}
|
|
1025
1090
|
}
|
|
@@ -1034,7 +1099,7 @@ const allowNameFromContent = function(el) {
|
|
|
1034
1099
|
}
|
|
1035
1100
|
};
|
|
1036
1101
|
|
|
1037
|
-
const getName = function(el, recursive, visited, directReference) {
|
|
1102
|
+
const getName = function(el, recursive, ongoingLabelledBy, visited, directReference) {
|
|
1038
1103
|
let ret = '';
|
|
1039
1104
|
|
|
1040
1105
|
visited = visited || [];
|
|
@@ -1050,23 +1115,23 @@ const getName = function(el, recursive, visited, directReference) {
|
|
|
1050
1115
|
// handled in atree
|
|
1051
1116
|
|
|
1052
1117
|
// B
|
|
1053
|
-
if (!
|
|
1118
|
+
if (!ongoingLabelledBy && el.matches('[aria-labelledby]')) {
|
|
1054
1119
|
const ids = el.getAttribute('aria-labelledby').split(/\s+/);
|
|
1055
1120
|
const strings = ids.map(id => {
|
|
1056
1121
|
const label = document.getElementById(id);
|
|
1057
|
-
return label ? getName(label, true, visited, true) : '';
|
|
1122
|
+
return label ? getName(label, true, true, visited, true) : '';
|
|
1058
1123
|
});
|
|
1059
1124
|
ret = strings.join(' ');
|
|
1060
1125
|
}
|
|
1061
1126
|
|
|
1062
1127
|
// E (the current draft has this at this high priority)
|
|
1063
1128
|
if (!ret.trim() && recursive) {
|
|
1064
|
-
if (query.matches(el, 'textbox
|
|
1129
|
+
if (query.matches(el, 'textbox')) {
|
|
1065
1130
|
ret = el.value || el.textContent;
|
|
1066
1131
|
} else if (query.matches(el, 'combobox,listbox')) {
|
|
1067
1132
|
const selected = query.querySelector(el, ':selected') || query.querySelector(el, 'option');
|
|
1068
1133
|
if (selected) {
|
|
1069
|
-
ret = getName(selected, recursive, visited);
|
|
1134
|
+
ret = getName(selected, recursive, ongoingLabelledBy, visited);
|
|
1070
1135
|
} else {
|
|
1071
1136
|
ret = el.value || '';
|
|
1072
1137
|
}
|
|
@@ -1084,13 +1149,10 @@ const getName = function(el, recursive, visited, directReference) {
|
|
|
1084
1149
|
// D
|
|
1085
1150
|
if (!ret.trim() && !recursive && el.labels) {
|
|
1086
1151
|
const strings = Array.prototype.map.call(el.labels, label => {
|
|
1087
|
-
return getName(label, true, visited);
|
|
1152
|
+
return getName(label, true, ongoingLabelledBy, visited);
|
|
1088
1153
|
});
|
|
1089
1154
|
ret = strings.join(' ');
|
|
1090
1155
|
}
|
|
1091
|
-
if (!ret.trim()) {
|
|
1092
|
-
ret = el.placeholder || '';
|
|
1093
|
-
}
|
|
1094
1156
|
if (!ret.trim()) {
|
|
1095
1157
|
ret = el.alt || '';
|
|
1096
1158
|
}
|
|
@@ -1102,7 +1164,7 @@ const getName = function(el, recursive, visited, directReference) {
|
|
|
1102
1164
|
if (el.matches(selector)) {
|
|
1103
1165
|
const descendant = el.querySelector(constants.nameFromDescendant[selector]);
|
|
1104
1166
|
if (descendant) {
|
|
1105
|
-
ret = getName(descendant, true, visited);
|
|
1167
|
+
ret = getName(descendant, true, ongoingLabelledBy, visited);
|
|
1106
1168
|
}
|
|
1107
1169
|
}
|
|
1108
1170
|
}
|
|
@@ -1113,11 +1175,14 @@ const getName = function(el, recursive, visited, directReference) {
|
|
|
1113
1175
|
ret = svgTitle.textContent;
|
|
1114
1176
|
}
|
|
1115
1177
|
}
|
|
1178
|
+
if (!ret.trim() && el.matches('a')) {
|
|
1179
|
+
ret = el.getAttribute('xlink:title') || '';
|
|
1180
|
+
}
|
|
1116
1181
|
|
|
1117
1182
|
// F
|
|
1118
1183
|
// FIXME: menu is not mentioned in the spec
|
|
1119
1184
|
if (!ret.trim() && (recursive || allowNameFromContent(el) || el.closest('label')) && !query.matches(el, 'menu')) {
|
|
1120
|
-
ret = getContent(el, visited);
|
|
1185
|
+
ret = getContent(el, ongoingLabelledBy, visited);
|
|
1121
1186
|
}
|
|
1122
1187
|
|
|
1123
1188
|
if (!ret.trim() && query.matches(el, 'button')) {
|
|
@@ -1136,9 +1201,8 @@ const getName = function(el, recursive, visited, directReference) {
|
|
|
1136
1201
|
// handled in getContent
|
|
1137
1202
|
|
|
1138
1203
|
// I
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
ret = el.title || '';
|
|
1204
|
+
if (!ret.trim()) {
|
|
1205
|
+
ret = el.title || el.placeholder || '';
|
|
1142
1206
|
}
|
|
1143
1207
|
|
|
1144
1208
|
// FIXME: not exactly sure about this, but it reduces the number of failing
|
|
@@ -1149,11 +1213,14 @@ const getName = function(el, recursive, visited, directReference) {
|
|
|
1149
1213
|
|
|
1150
1214
|
const before = getPseudoContent(el, ':before');
|
|
1151
1215
|
const after = getPseudoContent(el, ':after');
|
|
1152
|
-
return before + ret + after;
|
|
1216
|
+
return addSpaces(before + ret + after, el);
|
|
1153
1217
|
};
|
|
1154
1218
|
|
|
1155
1219
|
const getNameTrimmed = function(el) {
|
|
1156
|
-
return getName(el)
|
|
1220
|
+
return getName(el)
|
|
1221
|
+
.replace(/[ \n\r\t\f]+/g, ' ')
|
|
1222
|
+
.replace(/^ /, '')
|
|
1223
|
+
.replace(/ $/, '');
|
|
1157
1224
|
};
|
|
1158
1225
|
|
|
1159
1226
|
const getDescription = function(el) {
|
|
@@ -1163,7 +1230,7 @@ const getDescription = function(el) {
|
|
|
1163
1230
|
const ids = el.getAttribute('aria-describedby').split(/\s+/);
|
|
1164
1231
|
const strings = ids.map(id => {
|
|
1165
1232
|
const label = document.getElementById(id);
|
|
1166
|
-
return label ? getName(label, true) : '';
|
|
1233
|
+
return label ? getName(label, true, true) : '';
|
|
1167
1234
|
});
|
|
1168
1235
|
ret = strings.join(' ');
|
|
1169
1236
|
} else if (el.matches('[aria-description]')) {
|
|
@@ -1176,6 +1243,9 @@ const getDescription = function(el) {
|
|
|
1176
1243
|
} else if (el.title) {
|
|
1177
1244
|
ret = el.title;
|
|
1178
1245
|
}
|
|
1246
|
+
if (!ret.trim() && el.matches('a')) {
|
|
1247
|
+
ret = el.getAttribute('xlink:title') || '';
|
|
1248
|
+
}
|
|
1179
1249
|
|
|
1180
1250
|
ret = (ret || '').trim().replace(/\s+/g, ' ');
|
|
1181
1251
|
|
package/lib/attrs.js
CHANGED
|
@@ -68,7 +68,7 @@ const getAttribute = function(el, key) {
|
|
|
68
68
|
return true;
|
|
69
69
|
}
|
|
70
70
|
const style = window.getComputedStyle(el);
|
|
71
|
-
if (style.display === 'none' || style.visibility === 'hidden') {
|
|
71
|
+
if (style.display === 'none' || style.visibility === 'hidden' || style.visibility === 'collapse') {
|
|
72
72
|
return true;
|
|
73
73
|
}
|
|
74
74
|
}
|
package/lib/constants.js
CHANGED
|
@@ -3,14 +3,18 @@ exports.attributes = {
|
|
|
3
3
|
'activedescendant': 'id',
|
|
4
4
|
'atomic': 'bool',
|
|
5
5
|
'autocomplete': 'token',
|
|
6
|
+
'braillelabel': 'string',
|
|
7
|
+
'brailleroledescription': 'string',
|
|
6
8
|
'busy': 'bool',
|
|
7
9
|
'checked': 'tristate',
|
|
8
10
|
'colcount': 'int',
|
|
9
11
|
'colindex': 'int',
|
|
12
|
+
'colindextext': 'string',
|
|
10
13
|
'colspan': 'int',
|
|
11
14
|
'controls': 'id-list',
|
|
12
15
|
'current': 'token',
|
|
13
16
|
'describedby': 'id-list',
|
|
17
|
+
'description': 'string',
|
|
14
18
|
'details': 'id',
|
|
15
19
|
'disabled': 'bool',
|
|
16
20
|
'dropeffect': 'token-list',
|
|
@@ -40,6 +44,7 @@ exports.attributes = {
|
|
|
40
44
|
'roledescription': 'string',
|
|
41
45
|
'rowcount': 'int',
|
|
42
46
|
'rowindex': 'int',
|
|
47
|
+
'rowindextext': 'string',
|
|
43
48
|
'rowspan': 'int',
|
|
44
49
|
'selected': 'bool-undefined',
|
|
45
50
|
'setsize': 'int',
|
|
@@ -69,6 +74,19 @@ exports.attributeWeakMapping = {
|
|
|
69
74
|
// https://www.w3.org/TR/html/dom.html#sectioning-content-2
|
|
70
75
|
const scoped = ['article *', 'aside *', 'nav *', 'section *'].join(',');
|
|
71
76
|
|
|
77
|
+
const svgSelectors = function(selector) {
|
|
78
|
+
return [
|
|
79
|
+
// `${selector}:has(> title:not(:empty))`,
|
|
80
|
+
// `${selector}:has(> desc:not(:empty))`,
|
|
81
|
+
`${selector}[aria-label]`,
|
|
82
|
+
`${selector}[aria-roledescription]`,
|
|
83
|
+
`${selector}[aria-labelledby]`,
|
|
84
|
+
`${selector}[aria-describedby]`,
|
|
85
|
+
`${selector}[tabindex]`,
|
|
86
|
+
`${selector}[role]`,
|
|
87
|
+
];
|
|
88
|
+
};
|
|
89
|
+
|
|
72
90
|
// https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings
|
|
73
91
|
// https://www.w3.org/TR/wai-aria/roles
|
|
74
92
|
exports.roles = {
|
|
@@ -83,6 +101,7 @@ exports.roles = {
|
|
|
83
101
|
application: {},
|
|
84
102
|
article: {
|
|
85
103
|
selectors: ['article'],
|
|
104
|
+
childRoles: ['comment'],
|
|
86
105
|
},
|
|
87
106
|
banner: {
|
|
88
107
|
selectors: [`header:not(main *, ${scoped})`],
|
|
@@ -145,6 +164,9 @@ exports.roles = {
|
|
|
145
164
|
abstract: true,
|
|
146
165
|
childRoles: ['button', 'link', 'menuitem'],
|
|
147
166
|
},
|
|
167
|
+
comment: {
|
|
168
|
+
nameFromContents: true,
|
|
169
|
+
},
|
|
148
170
|
complementary: {
|
|
149
171
|
selectors: [
|
|
150
172
|
`aside:not(${scoped})`,
|
|
@@ -240,8 +262,8 @@ exports.roles = {
|
|
|
240
262
|
},
|
|
241
263
|
generic: {
|
|
242
264
|
selectors: [
|
|
243
|
-
'a:not([href])',
|
|
244
|
-
'area:not([href])',
|
|
265
|
+
'a:not([*|href])',
|
|
266
|
+
'area:not([*|href])',
|
|
245
267
|
`aside:not(${scoped}):not([aria-label]):not([aria-labelledby]):not([title])`,
|
|
246
268
|
'b',
|
|
247
269
|
'bdi',
|
|
@@ -265,8 +287,23 @@ exports.roles = {
|
|
|
265
287
|
'graphics-document': {
|
|
266
288
|
selectors: ['svg'],
|
|
267
289
|
},
|
|
268
|
-
'graphics-object': {
|
|
269
|
-
|
|
290
|
+
'graphics-object': {
|
|
291
|
+
selectors: [
|
|
292
|
+
...svgSelectors('symbol'),
|
|
293
|
+
...svgSelectors('use'),
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
'graphics-symbol': {
|
|
297
|
+
selectors: [
|
|
298
|
+
...svgSelectors('circle'),
|
|
299
|
+
...svgSelectors('ellipse'),
|
|
300
|
+
...svgSelectors('line'),
|
|
301
|
+
...svgSelectors('path'),
|
|
302
|
+
...svgSelectors('polygon'),
|
|
303
|
+
...svgSelectors('polyline'),
|
|
304
|
+
...svgSelectors('rect'),
|
|
305
|
+
],
|
|
306
|
+
},
|
|
270
307
|
grid: {
|
|
271
308
|
childRoles: ['treegrid'],
|
|
272
309
|
},
|
|
@@ -281,7 +318,11 @@ exports.roles = {
|
|
|
281
318
|
'fieldset',
|
|
282
319
|
'hgroup',
|
|
283
320
|
'optgroup',
|
|
321
|
+
...svgSelectors('foreignObject'),
|
|
322
|
+
...svgSelectors('g'),
|
|
284
323
|
'text',
|
|
324
|
+
...svgSelectors('textPath'),
|
|
325
|
+
...svgSelectors('tspan'),
|
|
285
326
|
],
|
|
286
327
|
childRoles: ['row', 'select', 'toolbar', 'graphics-object'],
|
|
287
328
|
},
|
|
@@ -292,8 +333,13 @@ exports.roles = {
|
|
|
292
333
|
'level': 2,
|
|
293
334
|
},
|
|
294
335
|
},
|
|
295
|
-
|
|
296
|
-
selectors: [
|
|
336
|
+
image: {
|
|
337
|
+
selectors: [
|
|
338
|
+
'img:not([alt=""])',
|
|
339
|
+
'graphics-symbol',
|
|
340
|
+
...svgSelectors('image'),
|
|
341
|
+
...svgSelectors('mesh'),
|
|
342
|
+
],
|
|
297
343
|
childRoles: ['doc-cover'],
|
|
298
344
|
},
|
|
299
345
|
input: {
|
|
@@ -341,7 +387,7 @@ exports.roles = {
|
|
|
341
387
|
],
|
|
342
388
|
},
|
|
343
389
|
link: {
|
|
344
|
-
selectors: ['a[href]', 'area[href]'],
|
|
390
|
+
selectors: ['a[*|href]', 'area[href]'],
|
|
345
391
|
childRoles: ['doc-backlink', 'doc-biblioref', 'doc-glossref', 'doc-noteref'],
|
|
346
392
|
nameFromContents: true,
|
|
347
393
|
},
|
|
@@ -371,6 +417,9 @@ exports.roles = {
|
|
|
371
417
|
main: {
|
|
372
418
|
selectors: ['main'],
|
|
373
419
|
},
|
|
420
|
+
mark: {
|
|
421
|
+
selectors: ['mark'],
|
|
422
|
+
},
|
|
374
423
|
marquee: {},
|
|
375
424
|
math: {
|
|
376
425
|
selectors: ['math'],
|
|
@@ -394,11 +443,10 @@ exports.roles = {
|
|
|
394
443
|
},
|
|
395
444
|
},
|
|
396
445
|
menuitem: {
|
|
397
|
-
childRoles: ['menuitemcheckbox'],
|
|
446
|
+
childRoles: ['menuitemcheckbox', 'menuitemradio'],
|
|
398
447
|
nameFromContents: true,
|
|
399
448
|
},
|
|
400
449
|
menuitemcheckbox: {
|
|
401
|
-
childRoles: ['menuitemradio'],
|
|
402
450
|
nameFromContents: true,
|
|
403
451
|
defaults: {
|
|
404
452
|
'checked': 'false',
|
|
@@ -505,12 +553,13 @@ exports.roles = {
|
|
|
505
553
|
'emphasis',
|
|
506
554
|
'figure',
|
|
507
555
|
'group',
|
|
508
|
-
'
|
|
556
|
+
'image',
|
|
509
557
|
'insertion',
|
|
510
558
|
'landmark',
|
|
511
559
|
'list',
|
|
512
560
|
'listitem',
|
|
513
561
|
'log',
|
|
562
|
+
'mark',
|
|
514
563
|
'marquee',
|
|
515
564
|
'math',
|
|
516
565
|
'note',
|
|
@@ -518,6 +567,7 @@ exports.roles = {
|
|
|
518
567
|
'status',
|
|
519
568
|
'strong',
|
|
520
569
|
'subscript',
|
|
570
|
+
'suggestion',
|
|
521
571
|
'superscript',
|
|
522
572
|
'table',
|
|
523
573
|
'tabpanel',
|
|
@@ -712,6 +762,7 @@ for (const role in exports.roles) {
|
|
|
712
762
|
exports.aliases = {
|
|
713
763
|
'presentation': 'none',
|
|
714
764
|
'directory': 'list',
|
|
765
|
+
'img': 'image',
|
|
715
766
|
};
|
|
716
767
|
|
|
717
768
|
exports.nameFromDescendant = {
|
package/lib/name.js
CHANGED
|
@@ -2,25 +2,43 @@ const constants = require('./constants.js');
|
|
|
2
2
|
const atree = require('./atree.js');
|
|
3
3
|
const query = require('./query.js');
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const inline = styles.display
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
const addSpaces = function(text, el, pseudoSelector) {
|
|
6
|
+
// https://github.com/w3c/accname/issues/3
|
|
7
|
+
const styles = window.getComputedStyle(el, pseudoSelector);
|
|
8
|
+
const inline = styles.display === 'inline';
|
|
9
|
+
return inline ? text : ` ${text} `;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const getPseudoContent = function(el, pseudoSelector) {
|
|
13
|
+
const styles = window.getComputedStyle(el, pseudoSelector);
|
|
14
|
+
let tail = styles.getPropertyValue('content').trim();
|
|
15
|
+
let ret = [];
|
|
16
|
+
|
|
17
|
+
let match;
|
|
18
|
+
while (tail.length) {
|
|
19
|
+
if (match = tail.match(/^"([^"]*)"/)) {
|
|
20
|
+
ret.push(match[1]);
|
|
21
|
+
} else if (match = tail.match(/^([a-z-]+)\(([^)]*)\)/)) {
|
|
22
|
+
if (match[1] === 'attr') {
|
|
23
|
+
ret.push(el.getAttribute(match[2]) || '');
|
|
24
|
+
}
|
|
25
|
+
} else if (match = tail.match(/^([a-z-]+)/)) {
|
|
26
|
+
if (match[1] === 'open-quote' || match[1] === 'close-quote') {
|
|
27
|
+
ret.push('"');
|
|
28
|
+
}
|
|
29
|
+
} else if (match = tail.match(/^\//)) {
|
|
30
|
+
ret = [];
|
|
17
31
|
} else {
|
|
18
|
-
|
|
32
|
+
// invalid content, ignore
|
|
33
|
+
return '';
|
|
19
34
|
}
|
|
35
|
+
tail = tail.slice(match[0].length).trim();
|
|
20
36
|
}
|
|
37
|
+
|
|
38
|
+
return addSpaces(ret.join(''), el, pseudoSelector);
|
|
21
39
|
};
|
|
22
40
|
|
|
23
|
-
const getContent = function(root, visited) {
|
|
41
|
+
const getContent = function(root, ongoingLabelledBy, visited) {
|
|
24
42
|
const children = atree.getChildNodes(root);
|
|
25
43
|
|
|
26
44
|
let ret = '';
|
|
@@ -31,12 +49,8 @@ const getContent = function(root, visited) {
|
|
|
31
49
|
} else if (node.nodeType === node.ELEMENT_NODE) {
|
|
32
50
|
if (node.tagName.toLowerCase() === 'br') {
|
|
33
51
|
ret += '\n';
|
|
34
|
-
} else if (window.getComputedStyle(node).display.substr(0, 6) === 'inline' &&
|
|
35
|
-
node.tagName.toLowerCase() !== 'input' &&
|
|
36
|
-
node.tagName.toLowerCase() !== 'img') { // https://github.com/w3c/accname/issues/3
|
|
37
|
-
ret += getName(node, true, visited);
|
|
38
52
|
} else {
|
|
39
|
-
ret +=
|
|
53
|
+
ret += getName(node, true, ongoingLabelledBy, visited);
|
|
40
54
|
}
|
|
41
55
|
}
|
|
42
56
|
}
|
|
@@ -51,7 +65,7 @@ const allowNameFromContent = function(el) {
|
|
|
51
65
|
}
|
|
52
66
|
};
|
|
53
67
|
|
|
54
|
-
const getName = function(el, recursive, visited, directReference) {
|
|
68
|
+
const getName = function(el, recursive, ongoingLabelledBy, visited, directReference) {
|
|
55
69
|
let ret = '';
|
|
56
70
|
|
|
57
71
|
visited = visited || [];
|
|
@@ -67,23 +81,23 @@ const getName = function(el, recursive, visited, directReference) {
|
|
|
67
81
|
// handled in atree
|
|
68
82
|
|
|
69
83
|
// B
|
|
70
|
-
if (!
|
|
84
|
+
if (!ongoingLabelledBy && el.matches('[aria-labelledby]')) {
|
|
71
85
|
const ids = el.getAttribute('aria-labelledby').split(/\s+/);
|
|
72
86
|
const strings = ids.map(id => {
|
|
73
87
|
const label = document.getElementById(id);
|
|
74
|
-
return label ? getName(label, true, visited, true) : '';
|
|
88
|
+
return label ? getName(label, true, true, visited, true) : '';
|
|
75
89
|
});
|
|
76
90
|
ret = strings.join(' ');
|
|
77
91
|
}
|
|
78
92
|
|
|
79
93
|
// E (the current draft has this at this high priority)
|
|
80
94
|
if (!ret.trim() && recursive) {
|
|
81
|
-
if (query.matches(el, 'textbox
|
|
95
|
+
if (query.matches(el, 'textbox')) {
|
|
82
96
|
ret = el.value || el.textContent;
|
|
83
97
|
} else if (query.matches(el, 'combobox,listbox')) {
|
|
84
98
|
const selected = query.querySelector(el, ':selected') || query.querySelector(el, 'option');
|
|
85
99
|
if (selected) {
|
|
86
|
-
ret = getName(selected, recursive, visited);
|
|
100
|
+
ret = getName(selected, recursive, ongoingLabelledBy, visited);
|
|
87
101
|
} else {
|
|
88
102
|
ret = el.value || '';
|
|
89
103
|
}
|
|
@@ -101,13 +115,10 @@ const getName = function(el, recursive, visited, directReference) {
|
|
|
101
115
|
// D
|
|
102
116
|
if (!ret.trim() && !recursive && el.labels) {
|
|
103
117
|
const strings = Array.prototype.map.call(el.labels, label => {
|
|
104
|
-
return getName(label, true, visited);
|
|
118
|
+
return getName(label, true, ongoingLabelledBy, visited);
|
|
105
119
|
});
|
|
106
120
|
ret = strings.join(' ');
|
|
107
121
|
}
|
|
108
|
-
if (!ret.trim()) {
|
|
109
|
-
ret = el.placeholder || '';
|
|
110
|
-
}
|
|
111
122
|
if (!ret.trim()) {
|
|
112
123
|
ret = el.alt || '';
|
|
113
124
|
}
|
|
@@ -119,7 +130,7 @@ const getName = function(el, recursive, visited, directReference) {
|
|
|
119
130
|
if (el.matches(selector)) {
|
|
120
131
|
const descendant = el.querySelector(constants.nameFromDescendant[selector]);
|
|
121
132
|
if (descendant) {
|
|
122
|
-
ret = getName(descendant, true, visited);
|
|
133
|
+
ret = getName(descendant, true, ongoingLabelledBy, visited);
|
|
123
134
|
}
|
|
124
135
|
}
|
|
125
136
|
}
|
|
@@ -130,11 +141,14 @@ const getName = function(el, recursive, visited, directReference) {
|
|
|
130
141
|
ret = svgTitle.textContent;
|
|
131
142
|
}
|
|
132
143
|
}
|
|
144
|
+
if (!ret.trim() && el.matches('a')) {
|
|
145
|
+
ret = el.getAttribute('xlink:title') || '';
|
|
146
|
+
}
|
|
133
147
|
|
|
134
148
|
// F
|
|
135
149
|
// FIXME: menu is not mentioned in the spec
|
|
136
150
|
if (!ret.trim() && (recursive || allowNameFromContent(el) || el.closest('label')) && !query.matches(el, 'menu')) {
|
|
137
|
-
ret = getContent(el, visited);
|
|
151
|
+
ret = getContent(el, ongoingLabelledBy, visited);
|
|
138
152
|
}
|
|
139
153
|
|
|
140
154
|
if (!ret.trim() && query.matches(el, 'button')) {
|
|
@@ -153,9 +167,8 @@ const getName = function(el, recursive, visited, directReference) {
|
|
|
153
167
|
// handled in getContent
|
|
154
168
|
|
|
155
169
|
// I
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
ret = el.title || '';
|
|
170
|
+
if (!ret.trim()) {
|
|
171
|
+
ret = el.title || el.placeholder || '';
|
|
159
172
|
}
|
|
160
173
|
|
|
161
174
|
// FIXME: not exactly sure about this, but it reduces the number of failing
|
|
@@ -166,11 +179,14 @@ const getName = function(el, recursive, visited, directReference) {
|
|
|
166
179
|
|
|
167
180
|
const before = getPseudoContent(el, ':before');
|
|
168
181
|
const after = getPseudoContent(el, ':after');
|
|
169
|
-
return before + ret + after;
|
|
182
|
+
return addSpaces(before + ret + after, el);
|
|
170
183
|
};
|
|
171
184
|
|
|
172
185
|
const getNameTrimmed = function(el) {
|
|
173
|
-
return getName(el)
|
|
186
|
+
return getName(el)
|
|
187
|
+
.replace(/[ \n\r\t\f]+/g, ' ')
|
|
188
|
+
.replace(/^ /, '')
|
|
189
|
+
.replace(/ $/, '');
|
|
174
190
|
};
|
|
175
191
|
|
|
176
192
|
const getDescription = function(el) {
|
|
@@ -180,7 +196,7 @@ const getDescription = function(el) {
|
|
|
180
196
|
const ids = el.getAttribute('aria-describedby').split(/\s+/);
|
|
181
197
|
const strings = ids.map(id => {
|
|
182
198
|
const label = document.getElementById(id);
|
|
183
|
-
return label ? getName(label, true) : '';
|
|
199
|
+
return label ? getName(label, true, true) : '';
|
|
184
200
|
});
|
|
185
201
|
ret = strings.join(' ');
|
|
186
202
|
} else if (el.matches('[aria-description]')) {
|
|
@@ -193,6 +209,9 @@ const getDescription = function(el) {
|
|
|
193
209
|
} else if (el.title) {
|
|
194
210
|
ret = el.title;
|
|
195
211
|
}
|
|
212
|
+
if (!ret.trim() && el.matches('a')) {
|
|
213
|
+
ret = el.getAttribute('xlink:title') || '';
|
|
214
|
+
}
|
|
196
215
|
|
|
197
216
|
ret = (ret || '').trim().replace(/\s+/g, ' ');
|
|
198
217
|
|