@whitesev/domutils 1.9.2 → 1.9.3
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/README.md +58 -58
- package/dist/index.amd.js +40 -22
- package/dist/index.amd.js.map +1 -1
- package/dist/index.amd.min.js +1 -1
- package/dist/index.amd.min.js.map +1 -1
- package/dist/index.cjs.js +40 -22
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.cjs.min.js +1 -1
- package/dist/index.cjs.min.js.map +1 -1
- package/dist/index.esm.js +40 -22
- package/dist/index.esm.js.map +1 -1
- package/dist/index.esm.min.js +1 -1
- package/dist/index.esm.min.js.map +1 -1
- package/dist/index.iife.js +40 -22
- package/dist/index.iife.js.map +1 -1
- package/dist/index.iife.min.js +1 -1
- package/dist/index.iife.min.js.map +1 -1
- package/dist/index.system.js +40 -22
- package/dist/index.system.js.map +1 -1
- package/dist/index.system.min.js +1 -1
- package/dist/index.system.min.js.map +1 -1
- package/dist/index.umd.js +40 -22
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/index.umd.min.js.map +1 -1
- package/dist/types/src/types/DOMUtilsCSSProperty.d.ts +36 -36
- package/dist/types/src/types/DOMUtilsEvent.d.ts +420 -420
- package/dist/types/src/types/WindowApi.d.ts +14 -14
- package/dist/types/src/types/env.d.ts +10 -10
- package/dist/types/src/types/global.d.ts +1 -1
- package/dist/types/src/types/gm.d.ts +2 -2
- package/index.ts +3 -3
- package/package.json +21 -22
- package/src/CommonUtils.ts +163 -163
- package/src/ElementAnimate.ts +290 -290
- package/src/ElementEvent.ts +1569 -1569
- package/src/ElementHandler.ts +43 -43
- package/src/ElementSelector.ts +307 -289
- package/src/ElementWait.ts +742 -742
- package/src/GlobalData.ts +5 -5
- package/src/OriginPrototype.ts +5 -5
- package/src/Utils.ts +388 -388
- package/src/WindowApi.ts +59 -59
- package/src/index.ts +2052 -2052
- package/src/types/DOMUtilsCSSProperty.d.ts +36 -36
- package/src/types/DOMUtilsEvent.d.ts +420 -420
- package/src/types/WindowApi.d.ts +14 -14
- package/src/types/env.d.ts +10 -10
- package/src/types/global.d.ts +1 -1
- package/src/types/gm.d.ts +2 -2
package/src/ElementSelector.ts
CHANGED
|
@@ -1,289 +1,307 @@
|
|
|
1
|
-
import type { WindowApiOption } from "./types/WindowApi";
|
|
2
|
-
import { WindowApi } from "./WindowApi";
|
|
3
|
-
|
|
4
|
-
class ElementSelector {
|
|
5
|
-
windowApi: typeof WindowApi.prototype;
|
|
6
|
-
constructor(windowApiOption?: WindowApiOption) {
|
|
7
|
-
this.windowApi = new WindowApi(windowApiOption);
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* 选择器,可使用以下的额外语法
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* **前置语法**
|
|
14
|
-
*
|
|
15
|
-
* + `xpath:` 作用:执行xpath语法,会对当前js环境下的`Document`进行节点获取
|
|
16
|
-
*
|
|
17
|
-
* **末尾语法**
|
|
18
|
-
* + `:contains([text])` 作用: 找到包含指定文本内容的指定元素
|
|
19
|
-
* + `:empty` 作用:找到既没有文本内容也没有子元素的指定元素
|
|
20
|
-
* + `:regexp([text])` 作用: 找到符合正则表达式的内容的指定元素
|
|
21
|
-
* @param selector 选择器
|
|
22
|
-
* @param parent 指定父元素,默认为`document`
|
|
23
|
-
* @example
|
|
24
|
-
* DOMUtils.selector("div:contains('测试')")
|
|
25
|
-
* > div.xxx
|
|
26
|
-
* @example
|
|
27
|
-
* DOMUtils.selector("div:empty")
|
|
28
|
-
* > div.xxx
|
|
29
|
-
* @example
|
|
30
|
-
* DOMUtils.selector("div:regexp('^xxxx$')")
|
|
31
|
-
* > div.xxx
|
|
32
|
-
*/
|
|
33
|
-
selector<K extends keyof HTMLElementTagNameMap>(
|
|
34
|
-
selector: K,
|
|
35
|
-
parent?: Element | Document | DocumentFragment | ShadowRoot
|
|
36
|
-
): HTMLElementTagNameMap[K] | undefined;
|
|
37
|
-
selector<E extends Element = HTMLElement>(
|
|
38
|
-
selector: string,
|
|
39
|
-
parent?: Element | Document | DocumentFragment | ShadowRoot
|
|
40
|
-
): E | undefined;
|
|
41
|
-
selector<E extends Element = HTMLElement>(
|
|
42
|
-
selector: string,
|
|
43
|
-
parent?: Element | Document | DocumentFragment | ShadowRoot
|
|
44
|
-
) {
|
|
45
|
-
return this.selectorAll<E>(selector, parent)[0];
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* 选择器,可使用以下的额外语法
|
|
49
|
-
*
|
|
50
|
-
* **前置语法**
|
|
51
|
-
*
|
|
52
|
-
* + `xpath:` 作用:执行xpath语法,会对当前js环境下的`Document`进行节点获取
|
|
53
|
-
*
|
|
54
|
-
* **末尾语法**
|
|
55
|
-
* + `:contains([text])` 作用: 找到包含指定文本内容的指定元素
|
|
56
|
-
* + `:empty` 作用:找到既没有文本内容也没有子元素的指定元素
|
|
57
|
-
* + `:regexp([text])` 作用: 找到符合正则表达式的内容的指定元素
|
|
58
|
-
* @param selector 选择器
|
|
59
|
-
* @param parent 指定父元素,默认为`document`
|
|
60
|
-
* @example
|
|
61
|
-
* DOMUtils.selectorAll("div:contains('测试')")
|
|
62
|
-
* > [div.xxx]
|
|
63
|
-
* @example
|
|
64
|
-
* DOMUtils.selectorAll("div:empty")
|
|
65
|
-
* > [div.xxx]
|
|
66
|
-
* @example
|
|
67
|
-
* DOMUtils.selectorAll("div:regexp('^xxxx$')")
|
|
68
|
-
* > [div.xxx]
|
|
69
|
-
* @example
|
|
70
|
-
* DOMUtils.selectorAll("div:regexp(/^xxx/ig)")
|
|
71
|
-
* > [div.xxx]
|
|
72
|
-
*/
|
|
73
|
-
selectorAll<K extends keyof HTMLElementTagNameMap>(
|
|
74
|
-
selector: K,
|
|
75
|
-
parent?: Element | Document | DocumentFragment | ShadowRoot
|
|
76
|
-
): HTMLElementTagNameMap[K][];
|
|
77
|
-
selectorAll<E extends Element = HTMLElement>(
|
|
78
|
-
selector: string,
|
|
79
|
-
parent?: Element | Document | DocumentFragment | ShadowRoot
|
|
80
|
-
): E[];
|
|
81
|
-
selectorAll<E extends Element = HTMLElement>(
|
|
82
|
-
selector: string,
|
|
83
|
-
parent?: Element | Document | DocumentFragment | ShadowRoot
|
|
84
|
-
) {
|
|
85
|
-
const context = this;
|
|
86
|
-
parent = parent || context.windowApi.document;
|
|
87
|
-
selector = selector.trim();
|
|
88
|
-
if (selector.startsWith("xpath:")) {
|
|
89
|
-
selector = selector.replace(/^xpath:/i, "");
|
|
90
|
-
const xpathResult = context.windowApi.document.evaluate(
|
|
91
|
-
selector,
|
|
92
|
-
parent,
|
|
93
|
-
null,
|
|
94
|
-
XPathResult.ORDERED_NODE_ITERATOR_TYPE,
|
|
95
|
-
null
|
|
96
|
-
);
|
|
97
|
-
const result: E[] = [];
|
|
98
|
-
let iterateNext = xpathResult.iterateNext();
|
|
99
|
-
while (iterateNext) {
|
|
100
|
-
result.push(iterateNext as E);
|
|
101
|
-
iterateNext = xpathResult.iterateNext();
|
|
102
|
-
}
|
|
103
|
-
return result;
|
|
104
|
-
} else if (selector.match(/[^\s]{1}:empty$/gi)) {
|
|
105
|
-
// empty 语法
|
|
106
|
-
selector = selector.replace(/:empty$/gi, "");
|
|
107
|
-
return Array.from(parent.querySelectorAll<E>(selector)).filter(($
|
|
108
|
-
return $
|
|
109
|
-
});
|
|
110
|
-
} else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
|
|
111
|
-
// contains 语法
|
|
112
|
-
const textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
|
|
113
|
-
const text = textMatch![2];
|
|
114
|
-
selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
|
|
115
|
-
return Array.from(parent.querySelectorAll<E>(selector)).filter(($
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
return Array.from(parent.querySelectorAll<E>(selector))
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
1
|
+
import type { WindowApiOption } from "./types/WindowApi";
|
|
2
|
+
import { WindowApi } from "./WindowApi";
|
|
3
|
+
|
|
4
|
+
class ElementSelector {
|
|
5
|
+
windowApi: typeof WindowApi.prototype;
|
|
6
|
+
constructor(windowApiOption?: WindowApiOption) {
|
|
7
|
+
this.windowApi = new WindowApi(windowApiOption);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 选择器,可使用以下的额外语法
|
|
11
|
+
*
|
|
12
|
+
*
|
|
13
|
+
* **前置语法**
|
|
14
|
+
*
|
|
15
|
+
* + `xpath:` 作用:执行xpath语法,会对当前js环境下的`Document`进行节点获取
|
|
16
|
+
*
|
|
17
|
+
* **末尾语法**
|
|
18
|
+
* + `:contains([text])` 作用: 找到包含指定文本内容的指定元素
|
|
19
|
+
* + `:empty` 作用:找到既没有文本内容也没有子元素的指定元素
|
|
20
|
+
* + `:regexp([text])` 作用: 找到符合正则表达式的内容的指定元素
|
|
21
|
+
* @param selector 选择器
|
|
22
|
+
* @param parent 指定父元素,默认为`document`
|
|
23
|
+
* @example
|
|
24
|
+
* DOMUtils.selector("div:contains('测试')")
|
|
25
|
+
* > div.xxx
|
|
26
|
+
* @example
|
|
27
|
+
* DOMUtils.selector("div:empty")
|
|
28
|
+
* > div.xxx
|
|
29
|
+
* @example
|
|
30
|
+
* DOMUtils.selector("div:regexp('^xxxx$')")
|
|
31
|
+
* > div.xxx
|
|
32
|
+
*/
|
|
33
|
+
selector<K extends keyof HTMLElementTagNameMap>(
|
|
34
|
+
selector: K,
|
|
35
|
+
parent?: Element | Document | DocumentFragment | ShadowRoot
|
|
36
|
+
): HTMLElementTagNameMap[K] | undefined;
|
|
37
|
+
selector<E extends Element = HTMLElement>(
|
|
38
|
+
selector: string,
|
|
39
|
+
parent?: Element | Document | DocumentFragment | ShadowRoot
|
|
40
|
+
): E | undefined;
|
|
41
|
+
selector<E extends Element = HTMLElement>(
|
|
42
|
+
selector: string,
|
|
43
|
+
parent?: Element | Document | DocumentFragment | ShadowRoot
|
|
44
|
+
) {
|
|
45
|
+
return this.selectorAll<E>(selector, parent)[0];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 选择器,可使用以下的额外语法
|
|
49
|
+
*
|
|
50
|
+
* **前置语法**
|
|
51
|
+
*
|
|
52
|
+
* + `xpath:` 作用:执行xpath语法,会对当前js环境下的`Document`进行节点获取
|
|
53
|
+
*
|
|
54
|
+
* **末尾语法**
|
|
55
|
+
* + `:contains([text])` 作用: 找到包含指定文本内容的指定元素
|
|
56
|
+
* + `:empty` 作用:找到既没有文本内容也没有子元素的指定元素
|
|
57
|
+
* + `:regexp([text])` 作用: 找到符合正则表达式的内容的指定元素
|
|
58
|
+
* @param selector 选择器
|
|
59
|
+
* @param parent 指定父元素,默认为`document`
|
|
60
|
+
* @example
|
|
61
|
+
* DOMUtils.selectorAll("div:contains('测试')")
|
|
62
|
+
* > [div.xxx]
|
|
63
|
+
* @example
|
|
64
|
+
* DOMUtils.selectorAll("div:empty")
|
|
65
|
+
* > [div.xxx]
|
|
66
|
+
* @example
|
|
67
|
+
* DOMUtils.selectorAll("div:regexp('^xxxx$')")
|
|
68
|
+
* > [div.xxx]
|
|
69
|
+
* @example
|
|
70
|
+
* DOMUtils.selectorAll("div:regexp(/^xxx/ig)")
|
|
71
|
+
* > [div.xxx]
|
|
72
|
+
*/
|
|
73
|
+
selectorAll<K extends keyof HTMLElementTagNameMap>(
|
|
74
|
+
selector: K,
|
|
75
|
+
parent?: Element | Document | DocumentFragment | ShadowRoot
|
|
76
|
+
): HTMLElementTagNameMap[K][];
|
|
77
|
+
selectorAll<E extends Element = HTMLElement>(
|
|
78
|
+
selector: string,
|
|
79
|
+
parent?: Element | Document | DocumentFragment | ShadowRoot
|
|
80
|
+
): E[];
|
|
81
|
+
selectorAll<E extends Element = HTMLElement>(
|
|
82
|
+
selector: string,
|
|
83
|
+
parent?: Element | Document | DocumentFragment | ShadowRoot
|
|
84
|
+
) {
|
|
85
|
+
const context = this;
|
|
86
|
+
parent = parent || context.windowApi.document;
|
|
87
|
+
selector = selector.trim();
|
|
88
|
+
if (selector.startsWith("xpath:")) {
|
|
89
|
+
selector = selector.replace(/^xpath:/i, "");
|
|
90
|
+
const xpathResult = context.windowApi.document.evaluate(
|
|
91
|
+
selector,
|
|
92
|
+
parent,
|
|
93
|
+
null,
|
|
94
|
+
XPathResult.ORDERED_NODE_ITERATOR_TYPE,
|
|
95
|
+
null
|
|
96
|
+
);
|
|
97
|
+
const result: E[] = [];
|
|
98
|
+
let iterateNext = xpathResult.iterateNext();
|
|
99
|
+
while (iterateNext) {
|
|
100
|
+
result.push(iterateNext as E);
|
|
101
|
+
iterateNext = xpathResult.iterateNext();
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
} else if (selector.match(/[^\s]{1}:empty$/gi)) {
|
|
105
|
+
// empty 语法
|
|
106
|
+
selector = selector.replace(/:empty$/gi, "");
|
|
107
|
+
return Array.from(parent.querySelectorAll<E>(selector)).filter(($elItem) => {
|
|
108
|
+
return $elItem?.innerHTML?.trim() === "";
|
|
109
|
+
});
|
|
110
|
+
} else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
|
|
111
|
+
// contains 语法
|
|
112
|
+
const textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
|
|
113
|
+
const text = textMatch![2];
|
|
114
|
+
selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
|
|
115
|
+
return Array.from(parent.querySelectorAll<E>(selector)).filter(($elItem) => {
|
|
116
|
+
let domText = $elItem.textContent;
|
|
117
|
+
if (domText == null) {
|
|
118
|
+
domText = ($elItem as any as HTMLElement).innerText;
|
|
119
|
+
}
|
|
120
|
+
if (domText == null) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
return domText.includes(text);
|
|
124
|
+
});
|
|
125
|
+
} else if (selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) || selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i)) {
|
|
126
|
+
// regexp 语法
|
|
127
|
+
const textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i);
|
|
128
|
+
let pattern = textMatch![2];
|
|
129
|
+
const flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i);
|
|
130
|
+
let flags = "";
|
|
131
|
+
if (flagMatch) {
|
|
132
|
+
pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, "");
|
|
133
|
+
flags = flagMatch[3];
|
|
134
|
+
}
|
|
135
|
+
const regexp = new RegExp(pattern, flags);
|
|
136
|
+
selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, "");
|
|
137
|
+
return Array.from(parent.querySelectorAll<E>(selector)).filter(($elItem) => {
|
|
138
|
+
let domText = $elItem.textContent;
|
|
139
|
+
if (domText == null) {
|
|
140
|
+
domText = ($elItem as any as HTMLElement).innerText;
|
|
141
|
+
}
|
|
142
|
+
if (domText == null) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
return !!domText.match(regexp);
|
|
146
|
+
});
|
|
147
|
+
} else {
|
|
148
|
+
// 普通语法
|
|
149
|
+
return Array.from(parent.querySelectorAll<E>(selector));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* 匹配元素,可使用以下的额外语法
|
|
154
|
+
*
|
|
155
|
+
* + :contains([text]) 作用: 找到包含指定文本内容的指定元素
|
|
156
|
+
* + :empty 作用:找到既没有文本内容也没有子元素的指定元素
|
|
157
|
+
* + :regexp([text]) 作用: 找到符合正则表达式的内容的指定元素
|
|
158
|
+
* @param $el 元素
|
|
159
|
+
* @param selector 选择器
|
|
160
|
+
* @example
|
|
161
|
+
* DOMUtils.matches("div:contains('测试')")
|
|
162
|
+
* > true
|
|
163
|
+
* @example
|
|
164
|
+
* DOMUtils.matches("div:empty")
|
|
165
|
+
* > true
|
|
166
|
+
* @example
|
|
167
|
+
* DOMUtils.matches("div:regexp('^xxxx$')")
|
|
168
|
+
* > true
|
|
169
|
+
* @example
|
|
170
|
+
* DOMUtils.matches("div:regexp(/^xxx/ig)")
|
|
171
|
+
* > false
|
|
172
|
+
*/
|
|
173
|
+
matches($el: HTMLElement | Element | null | undefined, selector: string): boolean {
|
|
174
|
+
selector = selector.trim();
|
|
175
|
+
if ($el == null) return false;
|
|
176
|
+
if ($el instanceof Document) return false;
|
|
177
|
+
|
|
178
|
+
if (selector.match(/[^\s]{1}:empty$/gi)) {
|
|
179
|
+
// empty 语法
|
|
180
|
+
selector = selector.replace(/:empty$/gi, "");
|
|
181
|
+
return $el.matches(selector) && $el?.innerHTML?.trim() === "";
|
|
182
|
+
} else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
|
|
183
|
+
// contains 语法
|
|
184
|
+
const textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
|
|
185
|
+
const text = textMatch![2];
|
|
186
|
+
selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
|
|
187
|
+
let domText = $el.textContent;
|
|
188
|
+
if (domText == null) {
|
|
189
|
+
domText = ($el as any as HTMLElement).innerText;
|
|
190
|
+
}
|
|
191
|
+
if (domText == null) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
return $el.matches(selector) && domText.includes(text);
|
|
195
|
+
} else if (selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) || selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i)) {
|
|
196
|
+
// regexp 语法
|
|
197
|
+
const textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i);
|
|
198
|
+
let pattern = textMatch![2];
|
|
199
|
+
const flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i);
|
|
200
|
+
let flags = "";
|
|
201
|
+
if (flagMatch) {
|
|
202
|
+
pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, "");
|
|
203
|
+
flags = flagMatch[3];
|
|
204
|
+
}
|
|
205
|
+
const regexp = new RegExp(pattern, flags);
|
|
206
|
+
selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, "");
|
|
207
|
+
let domText = $el.textContent;
|
|
208
|
+
if (domText == null) {
|
|
209
|
+
domText = ($el as any as HTMLElement).innerText;
|
|
210
|
+
}
|
|
211
|
+
if (domText == null) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
return $el.matches(selector) && !!domText.match(regexp);
|
|
215
|
+
} else {
|
|
216
|
+
// 普通语法
|
|
217
|
+
return $el.matches(selector);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* 根据选择器获取上层元素,可使用以下的额外语法
|
|
222
|
+
*
|
|
223
|
+
* + :contains([text]) 作用: 找到包含指定文本内容的指定元素
|
|
224
|
+
* + :empty 作用:找到既没有文本内容也没有子元素的指定元素
|
|
225
|
+
* + :regexp([text]) 作用: 找到符合正则表达式的内容的指定元素
|
|
226
|
+
* @param $el 元素
|
|
227
|
+
* @param selector 选择器
|
|
228
|
+
* @example
|
|
229
|
+
* DOMUtils.closest("div:contains('测试')")
|
|
230
|
+
* > div.xxx
|
|
231
|
+
* @example
|
|
232
|
+
* DOMUtils.closest("div:empty")
|
|
233
|
+
* > div.xxx
|
|
234
|
+
* @example
|
|
235
|
+
* DOMUtils.closest("div:regexp('^xxxx$')")
|
|
236
|
+
* > div.xxxx
|
|
237
|
+
* @example
|
|
238
|
+
* DOMUtils.closest("div:regexp(/^xxx/ig)")
|
|
239
|
+
* > null
|
|
240
|
+
*/
|
|
241
|
+
closest<K extends keyof HTMLElementTagNameMap>(
|
|
242
|
+
$el: HTMLElement | Element | null | undefined,
|
|
243
|
+
selector: string
|
|
244
|
+
): HTMLElementTagNameMap[K] | null;
|
|
245
|
+
closest<E extends Element = Element>($el: HTMLElement | Element | null | undefined, selector: string): E | null;
|
|
246
|
+
closest<E extends Element = Element>($el: HTMLElement | Element | null | undefined, selector: string): E | null {
|
|
247
|
+
selector = selector.trim();
|
|
248
|
+
|
|
249
|
+
if ($el == null) return null;
|
|
250
|
+
if ($el instanceof Document) return null;
|
|
251
|
+
|
|
252
|
+
if (selector.match(/[^\s]{1}:empty$/gi)) {
|
|
253
|
+
// empty 语法
|
|
254
|
+
selector = selector.replace(/:empty$/gi, "");
|
|
255
|
+
const $closest = $el?.closest<E>(selector);
|
|
256
|
+
if ($closest && $closest?.innerHTML?.trim() === "") {
|
|
257
|
+
return $closest;
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
260
|
+
} else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
|
|
261
|
+
// contains 语法
|
|
262
|
+
const textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
|
|
263
|
+
const text = textMatch![2];
|
|
264
|
+
selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
|
|
265
|
+
const $closest = $el?.closest<E>(selector);
|
|
266
|
+
if ($closest) {
|
|
267
|
+
// @ts-ignore
|
|
268
|
+
const content = $el?.textContent || $el?.innerText;
|
|
269
|
+
if (typeof content === "string" && content.includes(text)) {
|
|
270
|
+
return $closest;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return null;
|
|
274
|
+
} else if (selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) || selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i)) {
|
|
275
|
+
// regexp 语法
|
|
276
|
+
const textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i);
|
|
277
|
+
let pattern = textMatch![2];
|
|
278
|
+
const flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i);
|
|
279
|
+
let flags = "";
|
|
280
|
+
if (flagMatch) {
|
|
281
|
+
pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, "");
|
|
282
|
+
flags = flagMatch[3];
|
|
283
|
+
}
|
|
284
|
+
const regexp = new RegExp(pattern, flags);
|
|
285
|
+
selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, "");
|
|
286
|
+
const $closest = $el?.closest<E>(selector);
|
|
287
|
+
if ($closest) {
|
|
288
|
+
let domText = $el.textContent;
|
|
289
|
+
if (domText == null) {
|
|
290
|
+
domText = ($el as any as HTMLElement).innerText;
|
|
291
|
+
}
|
|
292
|
+
if (typeof domText === "string" && domText.match(regexp)) {
|
|
293
|
+
return $closest;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
297
|
+
} else {
|
|
298
|
+
// 普通语法
|
|
299
|
+
const $closest = $el?.closest<E>(selector);
|
|
300
|
+
return $closest;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const elementSelector = new ElementSelector();
|
|
306
|
+
|
|
307
|
+
export { elementSelector, ElementSelector };
|