@afixt/test-utils 1.1.7 → 1.2.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/.claude/settings.local.json +6 -1
- package/.github/workflows/ci.yml +71 -0
- package/.github/workflows/pr-check.yml +88 -0
- package/.github/workflows/security.yml +139 -0
- package/.husky/pre-commit +1 -0
- package/.jscpd.json +27 -0
- package/.markdownlint.json +9 -0
- package/.prettierignore +13 -0
- package/.prettierrc +10 -0
- package/docs/arrayUtils.js.html +2 -2
- package/docs/domUtils.js.html +2 -2
- package/docs/getAccessibleName.js.html +2 -2
- package/docs/getAccessibleText.js.html +2 -2
- package/docs/getAriaAttributesByElement.js.html +2 -2
- package/docs/getCSSGeneratedContent.js.html +2 -2
- package/docs/getComputedRole.js.html +2 -2
- package/docs/getFocusableElements.js.html +2 -2
- package/docs/getGeneratedContent.js.html +2 -2
- package/docs/getImageText.js.html +2 -2
- package/docs/getStyleObject.js.html +2 -2
- package/docs/global.html +2 -2
- package/docs/hasAccessibleName.js.html +2 -2
- package/docs/hasAttribute.js.html +2 -2
- package/docs/hasCSSGeneratedContent.js.html +2 -2
- package/docs/hasHiddenParent.js.html +2 -2
- package/docs/hasParent.js.html +2 -2
- package/docs/hasValidAriaAttributes.js.html +2 -2
- package/docs/hasValidAriaRole.js.html +2 -2
- package/docs/index.html +2 -2
- package/docs/index.js.html +2 -2
- package/docs/isAriaAttributesValid.js.html +2 -2
- package/docs/isComplexTable.js.html +2 -2
- package/docs/isDataTable.js.html +2 -2
- package/docs/isFocusable.js.html +2 -2
- package/docs/isHidden.js.html +2 -2
- package/docs/isOffScreen.js.html +2 -2
- package/docs/isValidUrl.js.html +2 -2
- package/docs/isVisible.js.html +2 -2
- package/docs/module-afixt-test-utils.html +2 -2
- package/docs/scripts/core.js +726 -726
- package/docs/scripts/core.min.js +22 -22
- package/docs/scripts/resize.js +90 -90
- package/docs/scripts/search.js +265 -265
- package/docs/scripts/third-party/Apache-License-2.0.txt +202 -202
- package/docs/scripts/third-party/fuse.js +8 -8
- package/docs/scripts/third-party/hljs-line-num-original.js +369 -369
- package/docs/scripts/third-party/hljs-original.js +5171 -5171
- package/docs/scripts/third-party/popper.js +5 -5
- package/docs/scripts/third-party/tippy.js +1 -1
- package/docs/scripts/third-party/tocbot.js +671 -671
- package/docs/styles/clean-jsdoc-theme-base.css +1159 -1159
- package/docs/styles/clean-jsdoc-theme-dark.css +412 -412
- package/docs/styles/clean-jsdoc-theme-light.css +482 -482
- package/docs/styles/clean-jsdoc-theme-scrollbar.css +29 -29
- package/docs/testContrast.js.html +2 -2
- package/docs/testLang.js.html +2 -2
- package/docs/testOrder.js.html +2 -2
- package/eslint.config.mjs +84 -0
- package/package.json +68 -41
- package/scratchpads/issue-6-standardize-repo.md +109 -0
- package/src/getAccessibleName.js +156 -112
- package/src/getAccessibleText.js +71 -42
- package/src/stringUtils.js +19 -21
- package/src/testContrast.js +103 -22
- package/test/getAccessibleName.test.js +379 -315
- package/test/getAccessibleText.test.js +375 -308
- package/test/stringUtils.test.js +376 -332
- package/test/testContrast.test.js +801 -651
- package/.eslintrc +0 -78
- package/.github/workflows/test.yml +0 -26
package/src/getAccessibleName.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const { isEmpty } = require(
|
|
2
|
-
const { getAccessibleText } = require(
|
|
1
|
+
const { isEmpty } = require('./stringUtils.js');
|
|
2
|
+
const { getAccessibleText } = require('./getAccessibleText.js');
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Gets the accessible name of an element according to the accessible name calculation algorithm
|
|
@@ -18,8 +18,7 @@ function getAccessibleName(element) {
|
|
|
18
18
|
// These are elements which are totally not able to be labeled at all.
|
|
19
19
|
// Even if the title attribute is valid per HTML for these elements,
|
|
20
20
|
// the title won't be used in any meaningful way by Accessibility APIs
|
|
21
|
-
const unlabellable =
|
|
22
|
-
"head *, hr, param, caption, colgroup, col, tbody, tfoot, thead, tr";
|
|
21
|
+
const unlabellable = 'head *, hr, param, caption, colgroup, col, tbody, tfoot, thead, tr';
|
|
23
22
|
|
|
24
23
|
// STEP 0 - verify item is visible and can be labelled
|
|
25
24
|
// if it isn't visible or can't be labelled then just bail
|
|
@@ -27,13 +26,15 @@ function getAccessibleName(element) {
|
|
|
27
26
|
return false;
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
let id
|
|
29
|
+
let id;
|
|
30
|
+
let ids;
|
|
31
|
+
let label;
|
|
31
32
|
|
|
32
33
|
// STEP 1 - always check for aria-labelledby first
|
|
33
34
|
// STEP 1.1 - if aria-labelledby exists, check that the referenced element exists
|
|
34
35
|
// STEP 1.1.1 - return the text from the referenced item
|
|
35
|
-
if (element.hasAttribute(
|
|
36
|
-
ids = element.getAttribute(
|
|
36
|
+
if (element.hasAttribute('aria-labelledby')) {
|
|
37
|
+
ids = element.getAttribute('aria-labelledby').trim().split(' ');
|
|
37
38
|
|
|
38
39
|
const text = [];
|
|
39
40
|
for (const id of ids) {
|
|
@@ -44,13 +45,13 @@ function getAccessibleName(element) {
|
|
|
44
45
|
text.push(getAccessibleText(labelElement));
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
return text.join(
|
|
48
|
+
return text.join(' ');
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
// STEP 2 - (next) always check for aria-label second
|
|
51
52
|
// STEP 2.1 - if aria-label exists, return the text in it
|
|
52
|
-
if (element.hasAttribute(
|
|
53
|
-
const ariaLabel = element.getAttribute(
|
|
53
|
+
if (element.hasAttribute('aria-label')) {
|
|
54
|
+
const ariaLabel = element.getAttribute('aria-label');
|
|
54
55
|
if (ariaLabel) {
|
|
55
56
|
return ariaLabel;
|
|
56
57
|
}
|
|
@@ -61,18 +62,35 @@ function getAccessibleName(element) {
|
|
|
61
62
|
// Most ARIA controls/ widgets rely on labelling per steps 1 & 2 above.
|
|
62
63
|
// If a label isn't found in Step 1 & 2, we can (for some roles) use DOM subtree content to find the label.
|
|
63
64
|
// We check all of those here.
|
|
64
|
-
if (element.hasAttribute(
|
|
65
|
-
const roleValue = element.getAttribute(
|
|
65
|
+
if (element.hasAttribute('role')) {
|
|
66
|
+
const roleValue = element.getAttribute('role');
|
|
66
67
|
const textRoles = [
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
'button',
|
|
69
|
+
'checkbox',
|
|
70
|
+
'columnheader',
|
|
71
|
+
'gridcell',
|
|
72
|
+
'heading',
|
|
73
|
+
'link',
|
|
74
|
+
'listitem',
|
|
75
|
+
'menuitem',
|
|
76
|
+
'menuitemcheckbox',
|
|
77
|
+
'menuitemradio',
|
|
78
|
+
'option',
|
|
79
|
+
'radio',
|
|
80
|
+
'row',
|
|
81
|
+
'rowgroup',
|
|
82
|
+
'rowheader',
|
|
83
|
+
'tab',
|
|
84
|
+
'tooltip',
|
|
85
|
+
'treeitem',
|
|
70
86
|
];
|
|
71
87
|
|
|
72
88
|
if (textRoles.includes(roleValue)) {
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
89
|
+
// Use getAccessibleText which handles both text nodes and
|
|
90
|
+
// image alt text in the subtree (not just textContent)
|
|
91
|
+
const text = getAccessibleText(element);
|
|
92
|
+
if (!isEmpty(text)) {
|
|
93
|
+
return text;
|
|
76
94
|
}
|
|
77
95
|
}
|
|
78
96
|
}
|
|
@@ -85,7 +103,8 @@ function getAccessibleName(element) {
|
|
|
85
103
|
// STEP 4.3 Otherwise use the title attribute
|
|
86
104
|
// STEP 4.4 - return false. If none of the above yield a usable text string there is no accessible name
|
|
87
105
|
if (
|
|
88
|
-
matchesSelector(
|
|
106
|
+
matchesSelector(
|
|
107
|
+
element,
|
|
89
108
|
'input:not([type]), input[type="text"], input[type="email"], input[type="password"], input[type="search"], input[type="tel"], input[type="url"], textarea'
|
|
90
109
|
)
|
|
91
110
|
) {
|
|
@@ -102,14 +121,14 @@ function getAccessibleName(element) {
|
|
|
102
121
|
}
|
|
103
122
|
|
|
104
123
|
// if the element's parent is a label, use the text in that
|
|
105
|
-
else if (element.closest(
|
|
106
|
-
return getAccessibleText(element.closest(
|
|
124
|
+
else if (element.closest('label')) {
|
|
125
|
+
return getAccessibleText(element.closest('label'));
|
|
107
126
|
}
|
|
108
127
|
|
|
109
128
|
// title attribute is last resort
|
|
110
|
-
else if (element.hasAttribute(
|
|
111
|
-
if (strlen(element.getAttribute(
|
|
112
|
-
return element.getAttribute(
|
|
129
|
+
else if (element.hasAttribute('title')) {
|
|
130
|
+
if (strlen(element.getAttribute('title')) > 0) {
|
|
131
|
+
return element.getAttribute('title');
|
|
113
132
|
} else {
|
|
114
133
|
return false;
|
|
115
134
|
}
|
|
@@ -129,17 +148,15 @@ function getAccessibleName(element) {
|
|
|
129
148
|
// STEP 5.4 For input type=button: return title attribute
|
|
130
149
|
// STEP 5.5 - return false. If none of the above yield a usable text string there is no accessible name
|
|
131
150
|
if (
|
|
132
|
-
matchesSelector(element,
|
|
133
|
-
'input[type="button"], input[type="submit"], input[type="reset"]'
|
|
134
|
-
)
|
|
151
|
+
matchesSelector(element, 'input[type="button"], input[type="submit"], input[type="reset"]')
|
|
135
152
|
) {
|
|
136
|
-
if (element.hasAttribute(
|
|
137
|
-
if (element.getAttribute(
|
|
138
|
-
return element.getAttribute(
|
|
153
|
+
if (element.hasAttribute('value')) {
|
|
154
|
+
if (element.getAttribute('value')) {
|
|
155
|
+
return element.getAttribute('value');
|
|
139
156
|
}
|
|
140
157
|
} else if (matchesSelector(element, 'input[type="button"]')) {
|
|
141
|
-
if (element.hasAttribute(
|
|
142
|
-
return element.getAttribute(
|
|
158
|
+
if (element.hasAttribute('title')) {
|
|
159
|
+
return element.getAttribute('title');
|
|
143
160
|
}
|
|
144
161
|
return false;
|
|
145
162
|
}
|
|
@@ -149,16 +166,16 @@ function getAccessibleName(element) {
|
|
|
149
166
|
// but we make a safe guess here anyway because getting the real name involves inspecting the Shadow DOM
|
|
150
167
|
// which we cannot do
|
|
151
168
|
else if (matchesSelector(element, 'input[type="submit"]')) {
|
|
152
|
-
if (element.hasAttribute(
|
|
153
|
-
return element.getAttribute(
|
|
169
|
+
if (element.hasAttribute('title')) {
|
|
170
|
+
return element.getAttribute('title');
|
|
154
171
|
} else {
|
|
155
|
-
return
|
|
172
|
+
return 'Submit';
|
|
156
173
|
}
|
|
157
174
|
} else if (matchesSelector(element, 'input[type="reset"]')) {
|
|
158
|
-
if (element.hasAttribute(
|
|
159
|
-
return element.getAttribute(
|
|
175
|
+
if (element.hasAttribute('title')) {
|
|
176
|
+
return element.getAttribute('title');
|
|
160
177
|
} else {
|
|
161
|
-
return
|
|
178
|
+
return 'Reset';
|
|
162
179
|
}
|
|
163
180
|
}
|
|
164
181
|
}
|
|
@@ -170,12 +187,12 @@ function getAccessibleName(element) {
|
|
|
170
187
|
// STEP 6.4: Otherwise use title attribute
|
|
171
188
|
// STEP 6.5: return false. If none of the above yield a usable text string there is no accessible name
|
|
172
189
|
if (matchesSelector(element, 'input[type="image"]')) {
|
|
173
|
-
if (element.hasAttribute(
|
|
174
|
-
return element.getAttribute(
|
|
175
|
-
} else if (element.hasAttribute(
|
|
176
|
-
return element.getAttribute(
|
|
177
|
-
} else if (element.hasAttribute(
|
|
178
|
-
return element.getAttribute(
|
|
190
|
+
if (element.hasAttribute('alt')) {
|
|
191
|
+
return element.getAttribute('alt');
|
|
192
|
+
} else if (element.hasAttribute('value')) {
|
|
193
|
+
return element.getAttribute('value');
|
|
194
|
+
} else if (element.hasAttribute('title')) {
|
|
195
|
+
return element.getAttribute('title');
|
|
179
196
|
} else {
|
|
180
197
|
return false;
|
|
181
198
|
}
|
|
@@ -185,11 +202,11 @@ function getAccessibleName(element) {
|
|
|
185
202
|
// STEP 7.1: use the button element subtree
|
|
186
203
|
// STEP 7.2: use title attribute
|
|
187
204
|
// STEP 7.3: return false. If none of the above yield a usable text string there is no accessible name
|
|
188
|
-
if (element.tagName.toLowerCase() ===
|
|
205
|
+
if (element.tagName.toLowerCase() === 'button') {
|
|
189
206
|
if (strlen(getAccessibleText(element)) > 0) {
|
|
190
207
|
return getAccessibleText(element);
|
|
191
|
-
} else if (element.hasAttribute(
|
|
192
|
-
return element.getAttribute(
|
|
208
|
+
} else if (element.hasAttribute('title')) {
|
|
209
|
+
return element.getAttribute('title');
|
|
193
210
|
} else {
|
|
194
211
|
return false;
|
|
195
212
|
}
|
|
@@ -201,7 +218,8 @@ function getAccessibleName(element) {
|
|
|
201
218
|
// STEP 8.3: return false. If none of the above yield a usable text
|
|
202
219
|
// string there is no accessible name
|
|
203
220
|
if (
|
|
204
|
-
matchesSelector(
|
|
221
|
+
matchesSelector(
|
|
222
|
+
element,
|
|
205
223
|
'select, input[type="checkbox"], input[type="color"], input[type="date"], input[type="datetime"], input[type="datetime-local"], input[type="email"], input[type="file"], input[type="month"], input[type="number"], input[type="radio"], input[type="range"], input[type="time"], input[type="week"]'
|
|
206
224
|
)
|
|
207
225
|
) {
|
|
@@ -217,13 +235,13 @@ function getAccessibleName(element) {
|
|
|
217
235
|
}
|
|
218
236
|
|
|
219
237
|
// if the element's parent is a label, use the text in that
|
|
220
|
-
else if (element.closest(
|
|
221
|
-
return getAccessibleText(element.closest(
|
|
238
|
+
else if (element.closest('label')) {
|
|
239
|
+
return getAccessibleText(element.closest('label'));
|
|
222
240
|
}
|
|
223
241
|
|
|
224
242
|
// title attribute is last resort
|
|
225
|
-
else if (element.hasAttribute(
|
|
226
|
-
return element.getAttribute(
|
|
243
|
+
else if (element.hasAttribute('title')) {
|
|
244
|
+
return element.getAttribute('title');
|
|
227
245
|
}
|
|
228
246
|
|
|
229
247
|
//if we got this far, there is no accessible name
|
|
@@ -242,20 +260,20 @@ function getAccessibleName(element) {
|
|
|
242
260
|
// And these return values are especially not accurate for
|
|
243
261
|
// non-english users, but we make a safe guess here anyway because
|
|
244
262
|
// like Submit buttons, getting the real value involves inspecting Shadow DOM
|
|
245
|
-
if (element.tagName.toLowerCase() ===
|
|
246
|
-
const summary = element.querySelector(
|
|
263
|
+
if (element.tagName.toLowerCase() === 'details') {
|
|
264
|
+
const summary = element.querySelector('summary');
|
|
247
265
|
if (summary) {
|
|
248
266
|
if (strlen(getAccessibleText(summary)) > 0) {
|
|
249
267
|
return getAccessibleText(summary);
|
|
250
268
|
}
|
|
251
|
-
} else if (element.hasAttribute(
|
|
252
|
-
if (strlen(element.getAttribute(
|
|
253
|
-
return element.getAttribute(
|
|
269
|
+
} else if (element.hasAttribute('title')) {
|
|
270
|
+
if (strlen(element.getAttribute('title')) > 0) {
|
|
271
|
+
return element.getAttribute('title');
|
|
254
272
|
} else {
|
|
255
|
-
return
|
|
273
|
+
return 'Details';
|
|
256
274
|
}
|
|
257
275
|
} else {
|
|
258
|
-
return
|
|
276
|
+
return 'Details';
|
|
259
277
|
}
|
|
260
278
|
}
|
|
261
279
|
|
|
@@ -264,14 +282,14 @@ function getAccessibleName(element) {
|
|
|
264
282
|
// STEP 10.2: title attribute
|
|
265
283
|
// STEP 10.3: return false. If none of the above yield a usable text
|
|
266
284
|
// string there is no accessible name
|
|
267
|
-
if (element.tagName.toLowerCase() ===
|
|
268
|
-
const figcaption = element.querySelector(
|
|
285
|
+
if (element.tagName.toLowerCase() === 'figure') {
|
|
286
|
+
const figcaption = element.querySelector('figcaption');
|
|
269
287
|
if (figcaption) {
|
|
270
288
|
if (strlen(getAccessibleText(figcaption)) > 0) {
|
|
271
289
|
return getAccessibleText(figcaption);
|
|
272
290
|
}
|
|
273
|
-
} else if (element.hasAttribute(
|
|
274
|
-
return element.getAttribute(
|
|
291
|
+
} else if (element.hasAttribute('title')) {
|
|
292
|
+
return element.getAttribute('title');
|
|
275
293
|
} else {
|
|
276
294
|
return false;
|
|
277
295
|
}
|
|
@@ -282,11 +300,11 @@ function getAccessibleName(element) {
|
|
|
282
300
|
// STEP 11.2: use title attribute
|
|
283
301
|
// STEP 11.3: return false. If none of the above yield a usable
|
|
284
302
|
// text string there is no accessible name
|
|
285
|
-
if (element.tagName.toLowerCase() ===
|
|
286
|
-
if (element.hasAttribute(
|
|
287
|
-
return element.getAttribute(
|
|
288
|
-
} else if (element.hasAttribute(
|
|
289
|
-
return element.getAttribute(
|
|
303
|
+
if (element.tagName.toLowerCase() === 'img') {
|
|
304
|
+
if (element.hasAttribute('alt')) {
|
|
305
|
+
return element.getAttribute('alt');
|
|
306
|
+
} else if (element.hasAttribute('title')) {
|
|
307
|
+
return element.getAttribute('title');
|
|
290
308
|
} else {
|
|
291
309
|
return false;
|
|
292
310
|
}
|
|
@@ -297,11 +315,11 @@ function getAccessibleName(element) {
|
|
|
297
315
|
// STEP 11-1.2: use title attribute
|
|
298
316
|
// STEP 11-1.3: return false. If none of the above yield a usable
|
|
299
317
|
// text string there is no accessible name
|
|
300
|
-
if (matchesSelector(element,
|
|
301
|
-
if (element.hasAttribute(
|
|
302
|
-
return element.getAttribute(
|
|
303
|
-
} else if (element.hasAttribute(
|
|
304
|
-
return element.getAttribute(
|
|
318
|
+
if (matchesSelector(element, 'area[href]')) {
|
|
319
|
+
if (element.hasAttribute('alt')) {
|
|
320
|
+
return element.getAttribute('alt');
|
|
321
|
+
} else if (element.hasAttribute('title')) {
|
|
322
|
+
return element.getAttribute('title');
|
|
305
323
|
} else {
|
|
306
324
|
return false;
|
|
307
325
|
}
|
|
@@ -311,9 +329,9 @@ function getAccessibleName(element) {
|
|
|
311
329
|
// STEP 11-2.1: use alt attribute
|
|
312
330
|
// STEP 11-2.3: return false. If none of the above yield a usable
|
|
313
331
|
// text string there is no accessible name
|
|
314
|
-
if (element.tagName.toLowerCase() ===
|
|
315
|
-
if (element.hasAttribute(
|
|
316
|
-
return element.getAttribute(
|
|
332
|
+
if (element.tagName.toLowerCase() === 'applet') {
|
|
333
|
+
if (element.hasAttribute('alt')) {
|
|
334
|
+
return element.getAttribute('alt');
|
|
317
335
|
} else {
|
|
318
336
|
return false;
|
|
319
337
|
}
|
|
@@ -325,9 +343,9 @@ function getAccessibleName(element) {
|
|
|
325
343
|
// title attribute, or fallback content (handled at end via getAccessibleText)
|
|
326
344
|
// STEP 11-3.1: use title attribute
|
|
327
345
|
// STEP 11-3.2: fallback content is handled at the end via getAccessibleText
|
|
328
|
-
if (element.tagName.toLowerCase() ===
|
|
329
|
-
if (element.hasAttribute(
|
|
330
|
-
const titleValue = element.getAttribute(
|
|
346
|
+
if (element.tagName.toLowerCase() === 'object') {
|
|
347
|
+
if (element.hasAttribute('title')) {
|
|
348
|
+
const titleValue = element.getAttribute('title');
|
|
331
349
|
if (strlen(titleValue) > 0) {
|
|
332
350
|
return titleValue;
|
|
333
351
|
}
|
|
@@ -343,16 +361,16 @@ function getAccessibleName(element) {
|
|
|
343
361
|
// STEP 11-4.1: use title attribute
|
|
344
362
|
// STEP 11-4.2: for native meter, use associated label
|
|
345
363
|
// STEP 11-4.3: return false (don't use text content)
|
|
346
|
-
if (element.tagName.toLowerCase() ===
|
|
347
|
-
if (element.hasAttribute(
|
|
348
|
-
const titleValue = element.getAttribute(
|
|
364
|
+
if (element.tagName.toLowerCase() === 'meter' || element.getAttribute('role') === 'meter') {
|
|
365
|
+
if (element.hasAttribute('title')) {
|
|
366
|
+
const titleValue = element.getAttribute('title');
|
|
349
367
|
if (strlen(titleValue) > 0) {
|
|
350
368
|
return titleValue;
|
|
351
369
|
}
|
|
352
370
|
}
|
|
353
371
|
|
|
354
372
|
// For native meter elements, check for associated label
|
|
355
|
-
if (element.tagName.toLowerCase() ===
|
|
373
|
+
if (element.tagName.toLowerCase() === 'meter') {
|
|
356
374
|
// Check for label with for attribute
|
|
357
375
|
if (element.id) {
|
|
358
376
|
const label = document.querySelector('label[for="' + element.id + '"]');
|
|
@@ -362,11 +380,11 @@ function getAccessibleName(element) {
|
|
|
362
380
|
}
|
|
363
381
|
|
|
364
382
|
// Check for wrapping label
|
|
365
|
-
const parentLabel = element.closest(
|
|
383
|
+
const parentLabel = element.closest('label');
|
|
366
384
|
if (parentLabel) {
|
|
367
385
|
// Get label text excluding the meter's own content
|
|
368
386
|
const clone = parentLabel.cloneNode(true);
|
|
369
|
-
const meterInClone = clone.querySelector(
|
|
387
|
+
const meterInClone = clone.querySelector('meter');
|
|
370
388
|
if (meterInClone) {
|
|
371
389
|
meterInClone.remove();
|
|
372
390
|
}
|
|
@@ -386,16 +404,16 @@ function getAccessibleName(element) {
|
|
|
386
404
|
// STEP 12.3: use the summary attribute
|
|
387
405
|
// STEP 12.4: return false. If none of the above yield a usable
|
|
388
406
|
// text string there is no accessible name
|
|
389
|
-
if (element.tagName.toLowerCase() ===
|
|
390
|
-
const caption = element.querySelector(
|
|
407
|
+
if (element.tagName.toLowerCase() === 'table') {
|
|
408
|
+
const caption = element.querySelector('caption');
|
|
391
409
|
if (caption) {
|
|
392
410
|
if (strlen(getAccessibleText(caption)) > 0) {
|
|
393
411
|
return getAccessibleText(caption);
|
|
394
412
|
}
|
|
395
|
-
} else if (element.hasAttribute(
|
|
396
|
-
return element.getAttribute(
|
|
397
|
-
} else if (element.hasAttribute(
|
|
398
|
-
return element.getAttribute(
|
|
413
|
+
} else if (element.hasAttribute('title')) {
|
|
414
|
+
return element.getAttribute('title');
|
|
415
|
+
} else if (element.hasAttribute('summary')) {
|
|
416
|
+
return element.getAttribute('summary');
|
|
399
417
|
}
|
|
400
418
|
return false;
|
|
401
419
|
}
|
|
@@ -405,11 +423,11 @@ function getAccessibleName(element) {
|
|
|
405
423
|
// STEP 13.2: the title attribute
|
|
406
424
|
// STEP 13.3: return false. If none of the above yield a usable
|
|
407
425
|
// text string there is no accessible name
|
|
408
|
-
if (matchesSelector(element,
|
|
426
|
+
if (matchesSelector(element, 'a[href]')) {
|
|
409
427
|
if (strlen(getAccessibleText(element)) > 0) {
|
|
410
428
|
return getAccessibleText(element);
|
|
411
|
-
} else if (element.hasAttribute(
|
|
412
|
-
return element.getAttribute(
|
|
429
|
+
} else if (element.hasAttribute('title')) {
|
|
430
|
+
return element.getAttribute('title');
|
|
413
431
|
} else {
|
|
414
432
|
return false;
|
|
415
433
|
}
|
|
@@ -423,14 +441,15 @@ function getAccessibleName(element) {
|
|
|
423
441
|
// STEP 14.2: return false. If none of the above yield a usable
|
|
424
442
|
// text string there is no accessible name
|
|
425
443
|
if (
|
|
426
|
-
matchesSelector(
|
|
427
|
-
|
|
444
|
+
matchesSelector(
|
|
445
|
+
element,
|
|
446
|
+
'em, strong, small, s, cite, q, dfn, abbr, time, code, var, samp, kbd, sub, sup, i, b, u, mark, ruby, rt, rp, bdi, bdo, br, wbr'
|
|
428
447
|
)
|
|
429
448
|
) {
|
|
430
449
|
if (strlen(element.textContent) > 0) {
|
|
431
450
|
return element.textContent;
|
|
432
|
-
} else if (element.hasAttribute(
|
|
433
|
-
return element.getAttribute(
|
|
451
|
+
} else if (element.hasAttribute('title')) {
|
|
452
|
+
return element.getAttribute('title');
|
|
434
453
|
} else {
|
|
435
454
|
return false;
|
|
436
455
|
}
|
|
@@ -442,13 +461,13 @@ function getAccessibleName(element) {
|
|
|
442
461
|
// via aria-labelledby, aria-label, or title attribute.
|
|
443
462
|
// Since steps 1 & 2 already checked aria-labelledby and aria-label,
|
|
444
463
|
// we only need to check title here, then return false.
|
|
445
|
-
const isNavigation =
|
|
446
|
-
element.getAttribute(
|
|
464
|
+
const isNavigation =
|
|
465
|
+
element.tagName.toLowerCase() === 'nav' || element.getAttribute('role') === 'navigation';
|
|
447
466
|
|
|
448
467
|
if (isNavigation) {
|
|
449
468
|
// Title attribute is valid for navigation landmarks
|
|
450
|
-
if (element.hasAttribute(
|
|
451
|
-
const titleValue = element.getAttribute(
|
|
469
|
+
if (element.hasAttribute('title')) {
|
|
470
|
+
const titleValue = element.getAttribute('title');
|
|
452
471
|
if (strlen(titleValue) > 0) {
|
|
453
472
|
return titleValue;
|
|
454
473
|
}
|
|
@@ -476,14 +495,34 @@ function getAccessibleName(element) {
|
|
|
476
495
|
*/
|
|
477
496
|
function isNotVisible(element) {
|
|
478
497
|
// Importing isVisible would be better, but for this standalone function we'll check it this way
|
|
479
|
-
if (!element)
|
|
498
|
+
if (!element) {
|
|
499
|
+
return true;
|
|
500
|
+
}
|
|
480
501
|
|
|
481
502
|
// These elements are inherently not visible
|
|
482
503
|
// Note: 'area' is NOT included here because area elements DO have accessible names
|
|
483
504
|
// via the alt attribute and should participate in accessible name calculation
|
|
484
505
|
const nonVisibleSelectors = [
|
|
485
|
-
'base',
|
|
486
|
-
'
|
|
506
|
+
'base',
|
|
507
|
+
'head',
|
|
508
|
+
'meta',
|
|
509
|
+
'title',
|
|
510
|
+
'link',
|
|
511
|
+
'style',
|
|
512
|
+
'script',
|
|
513
|
+
'br',
|
|
514
|
+
'nobr',
|
|
515
|
+
'col',
|
|
516
|
+
'embed',
|
|
517
|
+
'input[type="hidden"]',
|
|
518
|
+
'keygen',
|
|
519
|
+
'source',
|
|
520
|
+
'track',
|
|
521
|
+
'wbr',
|
|
522
|
+
'datalist',
|
|
523
|
+
'param',
|
|
524
|
+
'noframes',
|
|
525
|
+
'ruby > rp',
|
|
487
526
|
];
|
|
488
527
|
|
|
489
528
|
if (nonVisibleSelectors.some(selector => matchesSelector(element, selector))) {
|
|
@@ -493,12 +532,16 @@ function isNotVisible(element) {
|
|
|
493
532
|
// Check if display is none
|
|
494
533
|
const isElemDisplayed = el => window.getComputedStyle(el).display === 'none';
|
|
495
534
|
|
|
496
|
-
if (isElemDisplayed(element))
|
|
535
|
+
if (isElemDisplayed(element)) {
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
497
538
|
|
|
498
539
|
// Check parent elements
|
|
499
540
|
let parent = element.parentElement;
|
|
500
541
|
while (parent) {
|
|
501
|
-
if (isElemDisplayed(parent))
|
|
542
|
+
if (isElemDisplayed(parent)) {
|
|
543
|
+
return true;
|
|
544
|
+
}
|
|
502
545
|
parent = parent.parentElement;
|
|
503
546
|
}
|
|
504
547
|
|
|
@@ -512,12 +555,13 @@ function isNotVisible(element) {
|
|
|
512
555
|
* @returns {boolean} True if element matches the selector
|
|
513
556
|
*/
|
|
514
557
|
function matchesSelector(element, selector) {
|
|
515
|
-
if (!element)
|
|
558
|
+
if (!element) {
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
516
561
|
|
|
517
562
|
// Use the right matches function depending on browser support
|
|
518
|
-
const matchesMethod =
|
|
519
|
-
|
|
520
|
-
element.msMatchesSelector;
|
|
563
|
+
const matchesMethod =
|
|
564
|
+
element.matches || element.mozMatchesSelector || element.msMatchesSelector;
|
|
521
565
|
|
|
522
566
|
// Handle multiple selectors (comma-separated)
|
|
523
567
|
if (selector.includes(',')) {
|
|
@@ -533,7 +577,7 @@ function matchesSelector(element, selector) {
|
|
|
533
577
|
* @returns {number} The string length or 0
|
|
534
578
|
*/
|
|
535
579
|
function strlen(str) {
|
|
536
|
-
return typeof str ===
|
|
580
|
+
return typeof str === 'string' && !isEmpty(str.trim()) ? str.trim().length : 0;
|
|
537
581
|
}
|
|
538
582
|
|
|
539
583
|
// Export the function for CommonJS module usage
|