/
isTabbable.ts
106 lines (82 loc) · 2.21 KB
/
isTabbable.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import { getTabindex, is } from "./utils";
import { defaultOption } from "./option";
function isTabbable(node: Element, option = defaultOption) {
if (node.hasAttribute("disabled")) {
return false;
}
if (option.displayCheck) {
if (node.getClientRects().length <= 0) {
return false;
}
const style = getComputedStyle(node);
if (style.visibility === "hidden") {
return false;
}
}
if (node.hasAttribute("tabindex")) {
return getTabindex(node) >= 0;
}
if (
node.hasAttribute("contenteditable") &&
node.getAttribute("contenteditable") !== "false"
) {
return true;
}
if (
is("details", node.parentNode) &&
is("summary", node) &&
node.parentNode.querySelector("summary") === node
) {
return true;
}
if (is("details", node.parentNode) && !node.parentNode.open) {
return false;
}
if (is("details", node) && !node.querySelector("summary")) {
return true;
}
if (
(is("audio", node) || is("video", node)) &&
node.hasAttribute("controls")
) {
return true;
}
if (is("a", node) && node.hasAttribute("href")) {
return true;
}
if (
node.matches(
"fieldset[disabled] > legend:first-child :where(input, button, select, textarea)"
)
) {
return !node.closest("fieldset[disabled]")?.matches("fieldset[disabled] *");
}
const enabled = !node.matches("fieldset:disabled *");
if (is("button", node) && enabled) {
return true;
}
if (is("input", node) && enabled && node.type === "radio") {
if (!node.name) return true;
const name = CSS.escape(node.name);
if (node.form) {
const selector = `input[type="radio"][name=${name}]:checked`;
const checked = node.form.querySelector(selector);
return !checked || node === checked;
}
const root = node.getRootNode() as Element;
const selector = `input[type="radio"][name=${name}]:checked:not(form *)`;
const checked = root.querySelector(selector);
return !checked || node === checked;
}
if (is("input", node) && enabled) {
return true;
}
if (is("select", node) && enabled) {
return true;
}
if (is("textarea", node) && enabled) {
return true;
}
return false;
}
export { isTabbable };