@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.
Files changed (50) hide show
  1. package/README.md +58 -58
  2. package/dist/index.amd.js +40 -22
  3. package/dist/index.amd.js.map +1 -1
  4. package/dist/index.amd.min.js +1 -1
  5. package/dist/index.amd.min.js.map +1 -1
  6. package/dist/index.cjs.js +40 -22
  7. package/dist/index.cjs.js.map +1 -1
  8. package/dist/index.cjs.min.js +1 -1
  9. package/dist/index.cjs.min.js.map +1 -1
  10. package/dist/index.esm.js +40 -22
  11. package/dist/index.esm.js.map +1 -1
  12. package/dist/index.esm.min.js +1 -1
  13. package/dist/index.esm.min.js.map +1 -1
  14. package/dist/index.iife.js +40 -22
  15. package/dist/index.iife.js.map +1 -1
  16. package/dist/index.iife.min.js +1 -1
  17. package/dist/index.iife.min.js.map +1 -1
  18. package/dist/index.system.js +40 -22
  19. package/dist/index.system.js.map +1 -1
  20. package/dist/index.system.min.js +1 -1
  21. package/dist/index.system.min.js.map +1 -1
  22. package/dist/index.umd.js +40 -22
  23. package/dist/index.umd.js.map +1 -1
  24. package/dist/index.umd.min.js +1 -1
  25. package/dist/index.umd.min.js.map +1 -1
  26. package/dist/types/src/types/DOMUtilsCSSProperty.d.ts +36 -36
  27. package/dist/types/src/types/DOMUtilsEvent.d.ts +420 -420
  28. package/dist/types/src/types/WindowApi.d.ts +14 -14
  29. package/dist/types/src/types/env.d.ts +10 -10
  30. package/dist/types/src/types/global.d.ts +1 -1
  31. package/dist/types/src/types/gm.d.ts +2 -2
  32. package/index.ts +3 -3
  33. package/package.json +21 -22
  34. package/src/CommonUtils.ts +163 -163
  35. package/src/ElementAnimate.ts +290 -290
  36. package/src/ElementEvent.ts +1569 -1569
  37. package/src/ElementHandler.ts +43 -43
  38. package/src/ElementSelector.ts +307 -289
  39. package/src/ElementWait.ts +742 -742
  40. package/src/GlobalData.ts +5 -5
  41. package/src/OriginPrototype.ts +5 -5
  42. package/src/Utils.ts +388 -388
  43. package/src/WindowApi.ts +59 -59
  44. package/src/index.ts +2052 -2052
  45. package/src/types/DOMUtilsCSSProperty.d.ts +36 -36
  46. package/src/types/DOMUtilsEvent.d.ts +420 -420
  47. package/src/types/WindowApi.d.ts +14 -14
  48. package/src/types/env.d.ts +10 -10
  49. package/src/types/global.d.ts +1 -1
  50. package/src/types/gm.d.ts +2 -2
package/src/index.ts CHANGED
@@ -1,2052 +1,2052 @@
1
- import { version } from "../package.json";
2
- import { CommonUtils } from "./CommonUtils";
3
- import { ElementHandler } from "./ElementHandler";
4
- import { type DOMUtilsCSSProperty, type DOMUtilsCSSPropertyType } from "./types/DOMUtilsCSSProperty";
5
- import type { DOMUtilsCreateElementAttributesMap } from "./types/DOMUtilsEvent";
6
- import type { DOMUtilsTargetElementType } from "./types/global";
7
- import type { WindowApiOption } from "./types/WindowApi";
8
-
9
- class DOMUtils extends ElementHandler {
10
- constructor(option?: WindowApiOption) {
11
- super(option);
12
- }
13
- /** 版本号 */
14
- version = version;
15
- /**
16
- * 取消挂载在window下的DOMUtils并返回DOMUtils
17
- * @example
18
- * let DOMUtils = window.DOMUtils.noConflict()
19
- */
20
- noConflict() {
21
- const that = this;
22
- if (that.windowApi.window.DOMUtils) {
23
- CommonUtils.delete(window, "DOMUtils");
24
- }
25
- that.windowApi.window.DOMUtils = this;
26
- return this;
27
- }
28
- /**
29
- * 获取元素的属性值
30
- * @param $el 目标元素
31
- * @param attrName 属性名
32
- * @example
33
- * // 获取a.xx元素的href属性
34
- * DOMUtils.attr(document.querySelector("a.xx"),"href");
35
- * DOMUtils.attr("a.xx","href");
36
- * > https://xxxx....
37
- */
38
- attr($el: DOMUtilsTargetElementType | Element, attrName: string): string;
39
- /**
40
- * 设置元素的属性值
41
- * @param $el 目标元素
42
- * @param attrName 属性名
43
- * @param attrValue 属性值
44
- * @example
45
- * // 修改a.xx元素的href属性为abcd
46
- * DOMUtils.attr(document.querySelector("a.xx"),"href","abcd");
47
- * DOMUtils.attr("a.xx","href","abcd");
48
- */
49
- attr($el: DOMUtilsTargetElementType | Element, attrName: string, attrValue: string | boolean | number): void;
50
- attr($el: DOMUtilsTargetElementType | Element, attrName: string, attrValue?: any) {
51
- const that = this;
52
- if (typeof $el === "string") {
53
- $el = that.selectorAll($el);
54
- }
55
- if ($el == null) {
56
- return;
57
- }
58
- if (CommonUtils.isNodeList($el)) {
59
- if (attrValue == null) {
60
- // 获取属性
61
- return that.attr($el[0] as HTMLElement, attrName, attrValue);
62
- } else {
63
- // 设置属性
64
- $el.forEach(($elItem) => {
65
- that.attr($elItem as HTMLElement, attrName, attrValue);
66
- });
67
- return;
68
- }
69
- }
70
- if (attrValue == null) {
71
- return $el.getAttribute(attrName);
72
- } else {
73
- $el.setAttribute(attrName, attrValue);
74
- }
75
- }
76
- /**
77
- * 创建元素
78
- * @param tagName 标签名
79
- * @param property 属性
80
- * @param attributes 元素上的自定义属性
81
- * @example
82
- * // 创建一个DIV元素,且属性class为xxx
83
- * DOMUtils.createElement("div",undefined,{ class:"xxx" });
84
- * > <div class="xxx"></div>
85
- * @example
86
- * // 创建一个DIV元素
87
- * DOMUtils.createElement("div");
88
- * > <div></div>
89
- * @example
90
- * // 创建一个DIV元素
91
- * DOMUtils.createElement("div","测试");
92
- * > <div>测试</div>
93
- */
94
- createElement<K extends keyof HTMLElementTagNameMap>(
95
- /** 元素名 */
96
- tagName: K,
97
- /** 属性 */
98
- property?:
99
- | ({
100
- [P in keyof HTMLElementTagNameMap[K]]?: HTMLElementTagNameMap[K][P];
101
- } & {
102
- [key: string]: any;
103
- })
104
- | string,
105
- /** 自定义属性 */
106
- attributes?: DOMUtilsCreateElementAttributesMap
107
- ): HTMLElementTagNameMap[K];
108
- /**
109
- * 创建元素
110
- * @param tagName 自定义的标签名
111
- * @param property 属性
112
- * @param attributes 元素上的自定义属性
113
- * @example
114
- * // 创建一个custom-div元素,且属性class为xxx
115
- * DOMUtils.createElement("custom-div",undefined,{ class:"xxx" });
116
- * > <custom-div class="xxx"></custom-div>
117
- * @example
118
- * // 创建一个custom-div元素
119
- * DOMUtils.createElement("custom-div");
120
- * > <custom-div></custom-div>
121
- * @example
122
- * // 创建一个custom-div元素
123
- * DOMUtils.createElement("custom-div","测试");
124
- * > <custom-div>测试</custom-div>
125
- */
126
- createElement(
127
- /** 元素名 */
128
- tagName: string,
129
- /** 属性 */
130
- property?:
131
- | ({
132
- [P in keyof HTMLElement]?: HTMLElement[P];
133
- } & {
134
- [key: string]: any;
135
- })
136
- | string,
137
- /** 自定义属性 */
138
- attributes?: DOMUtilsCreateElementAttributesMap
139
- ): HTMLElement;
140
- createElement<K extends keyof HTMLElementTagNameMap>(
141
- /** 元素名 */
142
- tagName: K,
143
- /** 属性 */
144
- property?:
145
- | ({
146
- [P in keyof HTMLElementTagNameMap[K]]?: HTMLElementTagNameMap[K][P];
147
- } & {
148
- [key: string]: any;
149
- })
150
- | string,
151
- /** 自定义属性 */
152
- attributes?: DOMUtilsCreateElementAttributesMap
153
- ): HTMLElementTagNameMap[K] {
154
- const that = this;
155
- const $el = that.windowApi.document.createElement(tagName);
156
- if (typeof property === "string") {
157
- that.html($el, property);
158
- return $el;
159
- }
160
- if (property == null) {
161
- property = {};
162
- }
163
- if (attributes == null) {
164
- attributes = {};
165
- }
166
- Object.keys(property).forEach((key) => {
167
- const value = property[key];
168
- if (key === "innerHTML") {
169
- that.html($el, value);
170
- return;
171
- }
172
- (<any>$el)[key] = value;
173
- });
174
- Object.keys(attributes).forEach((key) => {
175
- let value = attributes[key];
176
- if (typeof value === "object") {
177
- /* object转字符串 */
178
- value = JSON.stringify(value);
179
- } else if (typeof value === "function") {
180
- /* function转字符串 */
181
- value = value.toString();
182
- }
183
- $el.setAttribute(key, value);
184
- });
185
- return $el;
186
- }
187
- /**
188
- * 获取元素的样式属性值
189
- * @param $el 目标元素
190
- * @param property 样式属性名或包含多个属性名和属性值的对象
191
- * @example
192
- * // 获取元素a.xx的CSS属性display
193
- * DOMUtils.css(document.querySelector("a.xx"),"display");
194
- * DOMUtils.css("a.xx","display");
195
- * > "none"
196
- * */
197
- css($el: DOMUtilsTargetElementType, property: DOMUtilsCSSPropertyType): string;
198
- /**
199
- * 获取元素的样式属性值
200
- * @param $el 目标元素
201
- * @param property 样式属性名或包含多个属性名和属性值的对象
202
- * @example
203
- * // 获取元素a.xx的CSS属性display
204
- * DOMUtils.css(document.querySelector("a.xx"),"display");
205
- * DOMUtils.css("a.xx","display");
206
- * > "none"
207
- * */
208
- css($el: DOMUtilsTargetElementType, property: string): string;
209
- /**
210
- * 设置元素的样式属性
211
- * @param $el 目标元素
212
- * @param property 样式属性名或包含多个属性名和属性值的对象
213
- * @param value 样式属性值
214
- * @example
215
- * // 设置元素a.xx的CSS属性display为block
216
- * DOMUtils.css(document.querySelector("a.xx"),"display","block");
217
- * DOMUtils.css(document.querySelector("a.xx"),"display","block !important");
218
- * DOMUtils.css("a.xx","display","block");
219
- * DOMUtils.css("a.xx","display","block !important");
220
- * @example
221
- * // 设置元素a.xx的CSS属性top为10px
222
- * DOMUtils.css(document.querySelector("a.xx"),"top","10px");
223
- * DOMUtils.css(document.querySelector("a.xx"),"top",10);
224
- * */
225
- css($el: DOMUtilsTargetElementType, property: DOMUtilsCSSPropertyType & string, value: string | number): string;
226
- /**
227
- * 设置元素的样式属性
228
- * @param $el 目标元素
229
- * @param property 样式属性名或包含多个属性名和属性值的对象
230
- * @param value 样式属性值
231
- * @example
232
- * // 设置元素a.xx的CSS属性display为block
233
- * DOMUtils.css(document.querySelector("a.xx"),{ display: "block" }});
234
- * DOMUtils.css(document.querySelector("a.xx"),{ display: "block !important" }});
235
- * @example
236
- * // 设置元素a.xx的CSS属性top为10px
237
- * DOMUtils.css(document.querySelector("a.xx"),{ top: "10px" });
238
- * DOMUtils.css(document.querySelector("a.xx"),{ top: 10 });
239
- * */
240
- css(
241
- $el: DOMUtilsTargetElementType,
242
- property:
243
- | DOMUtilsCSSProperty
244
- | {
245
- [key: string]: string | number;
246
- }
247
- | string
248
- ): string;
249
- css(
250
- $el: DOMUtilsTargetElementType,
251
- property: DOMUtilsCSSPropertyType | string | DOMUtilsCSSProperty,
252
- value?: string | number
253
- ) {
254
- const that = this;
255
- /**
256
- * 把纯数字没有px的加上
257
- */
258
- function handlePixe(propertyName: string, propertyValue: string | number) {
259
- const allowAddPixe = ["width", "height", "top", "left", "right", "bottom", "font-size"];
260
- if (typeof propertyValue === "number") {
261
- propertyValue = propertyValue.toString();
262
- }
263
- if (typeof propertyValue === "string" && allowAddPixe.includes(propertyName) && propertyValue.match(/[0-9]$/gi)) {
264
- propertyValue = propertyValue + "px";
265
- }
266
- return propertyValue;
267
- }
268
- if (typeof $el === "string") {
269
- $el = that.selectorAll($el);
270
- }
271
- if ($el == null) {
272
- return;
273
- }
274
- if (CommonUtils.isNodeList($el)) {
275
- if (typeof property === "string") {
276
- if (value == null) {
277
- // 获取属性
278
- return that.css($el[0] as HTMLElement, property);
279
- } else {
280
- // 设置属性
281
- $el.forEach(($elItem) => {
282
- that.css($elItem as HTMLElement, property);
283
- });
284
- return;
285
- }
286
- } else if (typeof property === "object") {
287
- // 设置属性
288
- $el.forEach(($elItem) => {
289
- that.css($elItem as HTMLElement, property as DOMUtilsCSSProperty);
290
- });
291
- return;
292
- }
293
- return;
294
- }
295
- const setStyleProperty = (propertyName: string, propertyValue: string | number) => {
296
- if (typeof propertyValue === "string" && propertyValue.trim().endsWith("!important")) {
297
- propertyValue = propertyValue
298
- .trim()
299
- .replace(/!important$/gi, "")
300
- .trim();
301
- $el.style.setProperty(propertyName, propertyValue, "important");
302
- } else {
303
- propertyValue = handlePixe(propertyName, propertyValue);
304
- $el.style.setProperty(propertyName, propertyValue);
305
- }
306
- };
307
- if (typeof property === "string") {
308
- if (value == null) {
309
- return that.windowApi.globalThis.getComputedStyle($el).getPropertyValue(property);
310
- } else {
311
- setStyleProperty(property, value);
312
- }
313
- } else if (typeof property === "object") {
314
- for (const prop in property) {
315
- const value = property[prop as keyof typeof property];
316
- setStyleProperty(prop, value!);
317
- }
318
- } else {
319
- // 其他情况
320
- throw new TypeError("property must be string or object");
321
- }
322
- }
323
- /**
324
- * 获取元素的文本内容,优先返回textContent
325
- * @param $el 目标元素
326
- * @returns 如果传入了text,则返回undefined;否则返回文本内容
327
- * @example
328
- * // 设置元素a.xx的文本内容为abcd
329
- * DOMUtils.text(document.querySelector("a.xx"),"abcd")
330
- * DOMUtils.text("a.xx","abcd")
331
- * DOMUtils.text("a.xx",document.querySelector("b"))
332
- * */
333
- text($el: DOMUtilsTargetElementType | Element | DocumentFragment | Node): string;
334
- /**
335
- * 设置元素的文本内容
336
- * @param $el 目标元素
337
- * @param text (可选)文本内容
338
- * @returns 如果传入了text,则返回undefined;否则返回文本内容
339
- * @example
340
- * // 设置元素a.xx的文本内容为abcd
341
- * DOMUtils.text(document.querySelector("a.xx"),"abcd")
342
- * DOMUtils.text("a.xx","abcd")
343
- * DOMUtils.text("a.xx",document.querySelector("b"))
344
- * */
345
- text(
346
- $el: DOMUtilsTargetElementType | Element | DocumentFragment | Node,
347
- text: string | HTMLElement | Element | number
348
- ): void;
349
- text($el: DOMUtilsTargetElementType | Element | DocumentFragment | Node, text?: any) {
350
- const that = this;
351
- if (typeof $el === "string") {
352
- $el = that.selectorAll($el);
353
- }
354
- if ($el == null) {
355
- return;
356
- }
357
- if (CommonUtils.isNodeList($el)) {
358
- if (text == null) {
359
- // 获取
360
- return that.text($el[0] as HTMLElement);
361
- } else {
362
- // 设置
363
- $el.forEach(($elItem) => {
364
- that.text($elItem as HTMLElement, text);
365
- });
366
- }
367
- return;
368
- }
369
- if (text == null) {
370
- return $el.textContent || (<HTMLElement>$el).innerText;
371
- } else {
372
- if (text instanceof Node) {
373
- text = text.textContent || (text as HTMLElement).innerText;
374
- }
375
- if ("textContent" in $el) {
376
- $el.textContent = text as string;
377
- } else if ("innerText" in $el) {
378
- ($el as HTMLElement).innerText = text as string;
379
- }
380
- }
381
- }
382
- /**
383
- * 设置元素的HTML内容
384
- * @param element 目标元素
385
- * @param html (可选)HTML内容|元素
386
- * @returns 如果传入了html,则返回undefined;否则返回HTML内容
387
- * @example
388
- * // 设置元素a.xx的文本内容为<b>abcd</b>
389
- * DOMUtils.html(document.querySelector("a.xx"),"<b>abcd</b>")
390
- * DOMUtils.html("a.xx","<b>abcd</b>")
391
- * DOMUtils.html("a.xx",document.querySelector("b"))
392
- * */
393
- html(element: DOMUtilsTargetElementType, html: string | HTMLElement | Element | number): void;
394
- /**
395
- * 获取元素的HTML内容
396
- * @param $el 目标元素
397
- * @param html (可选)HTML内容|元素
398
- * @returns 如果传入了html,则返回undefined;否则返回HTML内容
399
- * @example
400
- * // 设置元素a.xx的文本内容为<b>abcd</b>
401
- * DOMUtils.html(document.querySelector("a.xx"),"<b>abcd</b>")
402
- * DOMUtils.html("a.xx","<b>abcd</b>")
403
- * DOMUtils.html("a.xx",document.querySelector("b"))
404
- * */
405
- html($el: DOMUtilsTargetElementType): string;
406
- html($el: DOMUtilsTargetElementType, html?: any) {
407
- const that = this;
408
- if (typeof $el === "string") {
409
- $el = that.selectorAll($el);
410
- }
411
- if ($el == null) {
412
- return;
413
- }
414
- if (CommonUtils.isNodeList($el)) {
415
- if (html == null) {
416
- // 获取
417
- return that.html($el[0] as HTMLElement);
418
- } else {
419
- // 设置
420
- $el.forEach(($elItem) => {
421
- that.html($elItem as HTMLElement, html);
422
- });
423
- }
424
- return;
425
- }
426
- if (html == null) {
427
- // 获取
428
- return $el.innerHTML;
429
- } else {
430
- // 设置
431
- if (html instanceof Element) {
432
- html = html.innerHTML;
433
- }
434
- if ("innerHTML" in $el) {
435
- CommonUtils.setSafeHTML($el, html);
436
- }
437
- }
438
- }
439
- /**
440
- * 获取移动元素的transform偏移
441
- */
442
- getTransform(
443
- $el: HTMLElement,
444
- isShow: boolean = false
445
- ): {
446
- transformLeft: number;
447
- transformTop: number;
448
- } {
449
- const that = this;
450
- let transform_left = 0;
451
- let transform_top = 0;
452
- if (!(isShow || (!isShow && CommonUtils.isShow($el)))) {
453
- /* 未显示 */
454
- const { recovery } = CommonUtils.forceShow($el);
455
- const transformInfo = that.getTransform($el, true);
456
- recovery();
457
- return transformInfo;
458
- }
459
- const elementTransform = that.windowApi.globalThis.getComputedStyle($el).transform;
460
- if (elementTransform != null && elementTransform !== "none" && elementTransform !== "") {
461
- const elementTransformSplit = elementTransform.match(/\((.+)\)/)?.[1].split(",");
462
- if (elementTransformSplit) {
463
- transform_left = Math.abs(parseInt(elementTransformSplit[4]));
464
- transform_top = Math.abs(parseInt(elementTransformSplit[5]));
465
- } else {
466
- transform_left = 0;
467
- transform_top = 0;
468
- }
469
- }
470
- return {
471
- transformLeft: transform_left,
472
- transformTop: transform_top,
473
- };
474
- }
475
-
476
- /**
477
- * 设置元素的value属性值
478
- * @param $el 目标元素
479
- * @param value (可选)value属性值
480
- * @returns 如果传入了value,则返回undefined;否则返回value属性值
481
- * > true
482
- * @example
483
- * // 修改元素input.xx的复选框值为true
484
- * DOMUtils.val(document.querySelector("input.xx"),true)
485
- * DOMUtils.val("input.xx",true)
486
- * */
487
- val(
488
- $el:
489
- | HTMLInputElement
490
- | HTMLTextAreaElement
491
- | HTMLSelectElement
492
- | string
493
- | (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement)[]
494
- | NodeListOf<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,
495
- value: string | boolean | number
496
- ): void;
497
- /**
498
- * 获取value属性值
499
- * @param $el 目标元素
500
- * @example
501
- * // 获取元素textarea的值
502
- * DOMUtils.val(document.querySelector("textarea.xx"))
503
- * */
504
- val(
505
- $el:
506
- | HTMLInputElement
507
- | HTMLTextAreaElement
508
- | HTMLSelectElement
509
- | string
510
- | (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement)[]
511
- | NodeListOf<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
512
- ): string;
513
- /**
514
- * 获取value属性值
515
- * @param $el 目标元素
516
- * @example
517
- * // 获取元素input.xx的复选框值
518
- * DOMUtils.val(document.querySelector("input.xx"))
519
- * DOMUtils.val("input.xx")
520
- * */
521
- val(
522
- $el:
523
- | HTMLInputElement
524
- | HTMLTextAreaElement
525
- | HTMLSelectElement
526
- | (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement)[]
527
- | NodeListOf<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
528
- ): boolean | string;
529
- val(
530
- $el:
531
- | HTMLInputElement
532
- | HTMLTextAreaElement
533
- | HTMLSelectElement
534
- | string
535
- | (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement)[]
536
- | NodeListOf<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,
537
- value?: string | boolean | number
538
- ) {
539
- const that = this;
540
- if (typeof $el === "string") {
541
- $el = that.selectorAll($el);
542
- }
543
- if ($el == null) {
544
- return;
545
- }
546
- if (CommonUtils.isNodeList($el)) {
547
- if (value == null) {
548
- // 获取
549
- return that.val($el[0] as HTMLInputElement);
550
- } else {
551
- // 设置
552
- $el.forEach(($elItem) => {
553
- that.val($elItem as HTMLInputElement, value);
554
- });
555
- }
556
- return;
557
- }
558
- if (value == null) {
559
- // 获取
560
- if ($el.localName === "input" && ($el.type === "checkbox" || $el.type === "radio")) {
561
- return ($el as HTMLInputElement).checked;
562
- } else {
563
- return $el.value;
564
- }
565
- } else {
566
- // 设置
567
- if ($el.localName === "input" && ($el.type === "checkbox" || $el.type === "radio")) {
568
- ($el as HTMLInputElement).checked = !!value;
569
- } else {
570
- $el.value = value as string;
571
- }
572
- }
573
- }
574
- /**
575
- * 获取元素的属性值
576
- * @param $el 目标元素
577
- * @param propName 属性名
578
- * @param propValue 属性值
579
- * @example
580
- * // 获取元素a.xx的属性data-value
581
- * DOMUtils.val(document.querySelector("a.xx"),"data-value")
582
- * DOMUtils.val("a.xx","data-value")
583
- * > undefined
584
- * */
585
- prop<T>($el: DOMUtilsTargetElementType | DocumentFragment, propName: string): T;
586
- /**
587
- * 设置元素的属性值
588
- * @param $el 目标元素
589
- * @param propName 属性名
590
- * @param propValue 属性值
591
- * @example
592
- * // 设置元素a.xx的属性data-value为1
593
- * DOMUtils.val(document.querySelector("a.xx"),"data-value",1)
594
- * DOMUtils.val("a.xx","data-value",1)
595
- * */
596
- prop<T>($el: DOMUtilsTargetElementType | DocumentFragment, propName: string, propValue: T): void;
597
- prop($el: DOMUtilsTargetElementType | DocumentFragment, propName: string, propValue?: any) {
598
- const that = this;
599
- if (typeof $el === "string") {
600
- $el = that.selectorAll($el);
601
- }
602
- if ($el == null) {
603
- return;
604
- }
605
- if (CommonUtils.isNodeList($el)) {
606
- if (propValue == null) {
607
- // 获取
608
- return that.prop($el[0] as HTMLElement, propName);
609
- } else {
610
- // 设置
611
- $el.forEach(($elItem) => {
612
- that.prop($elItem as HTMLElement, propName, propValue);
613
- });
614
- }
615
- return;
616
- }
617
- if (propValue == null) {
618
- return Reflect.get($el, propName);
619
- } else {
620
- if ($el instanceof Element && propName === "innerHTML") {
621
- that.html($el, propValue);
622
- } else {
623
- Reflect.set($el, propName, propValue);
624
- }
625
- }
626
- }
627
- /**
628
- * 移除元素的属性
629
- * @param $el 目标元素
630
- * @param attrName 属性名
631
- * @example
632
- * // 移除元素a.xx的属性data-value
633
- * DOMUtils.removeAttr(document.querySelector("a.xx"),"data-value")
634
- * DOMUtils.removeAttr("a.xx","data-value")
635
- * */
636
- removeAttr($el: DOMUtilsTargetElementType | Element, attrName: string) {
637
- const that = this;
638
- if (typeof $el === "string") {
639
- $el = that.selectorAll($el);
640
- }
641
- if ($el == null) {
642
- return;
643
- }
644
- if (CommonUtils.isNodeList($el)) {
645
- // 设置
646
- $el.forEach(($elItem) => {
647
- that.removeAttr($elItem as HTMLElement, attrName);
648
- });
649
- return;
650
- }
651
- $el.removeAttribute(attrName);
652
- }
653
- /**
654
- * 移除元素class名
655
- * @param $el 目标元素
656
- * @param className 类名
657
- * @example
658
- * // 移除元素a.xx的className为xx
659
- * DOMUtils.removeClass(document.querySelector("a.xx"),"xx")
660
- * DOMUtils.removeClass("a.xx","xx")
661
- */
662
- removeClass($el: DOMUtilsTargetElementType | Element, className?: string | string[] | undefined | null) {
663
- const that = this;
664
- if (typeof $el === "string") {
665
- $el = that.selectorAll($el);
666
- }
667
- if ($el == null) {
668
- return;
669
- }
670
- if (CommonUtils.isNodeList($el)) {
671
- // 设置
672
- $el.forEach(($elItem) => {
673
- that.removeClass($elItem as HTMLElement, className);
674
- });
675
- return;
676
- }
677
- if (className == null) {
678
- // 清空全部className
679
- $el.className = "";
680
- } else {
681
- if (!Array.isArray(className)) {
682
- className = className.trim().split(" ");
683
- }
684
- className.forEach((itemClassName) => {
685
- $el.classList.remove(itemClassName);
686
- });
687
- }
688
- }
689
- /**
690
- * 移除元素的属性
691
- * @param $el 目标元素
692
- * @param propName 属性名
693
- * @example
694
- * // 移除元素a.xx的href属性
695
- * DOMUtils.removeProp(document.querySelector("a.xx"),"href")
696
- * DOMUtils.removeProp("a.xx","href")
697
- * */
698
- removeProp($el: DOMUtilsTargetElementType | DocumentFragment, propName: string) {
699
- const that = this;
700
- if (typeof $el === "string") {
701
- $el = that.selectorAll($el);
702
- }
703
- if ($el == null) {
704
- return;
705
- }
706
- if (CommonUtils.isNodeList($el)) {
707
- // 设置
708
- $el.forEach(($elItem) => {
709
- that.removeProp($elItem as HTMLElement, propName);
710
- });
711
- return;
712
- }
713
- CommonUtils.delete($el, propName);
714
- }
715
- /**
716
- * 给元素添加class
717
- * @param $el 目标元素
718
- * @param className class名
719
- * @example
720
- * // 元素a.xx的className添加_vue_
721
- * DOMUtils.addClass(document.querySelector("a.xx"),"_vue_")
722
- * DOMUtils.addClass("a.xx","_vue_")
723
- * */
724
- addClass($el: DOMUtilsTargetElementType | Element, className: string | string[]) {
725
- const that = this;
726
- if (typeof $el === "string") {
727
- $el = that.selectorAll($el);
728
- }
729
- if ($el == null) {
730
- return;
731
- }
732
- if (CommonUtils.isNodeList($el)) {
733
- // 设置
734
- $el.forEach(($elItem) => {
735
- that.addClass($elItem as HTMLElement, className);
736
- });
737
- return;
738
- }
739
- if (!Array.isArray(className)) {
740
- className = className.split(" ");
741
- }
742
- className.forEach((itemClassName) => {
743
- if (itemClassName.trim() == "") {
744
- return;
745
- }
746
- $el.classList.add(itemClassName);
747
- });
748
- }
749
- /**
750
- * 判断元素是否存在className
751
- * @param $el
752
- * @param className
753
- */
754
- hasClass($el: DOMUtilsTargetElementType | Element, className: string | string[]): boolean {
755
- const that = this;
756
- if (typeof $el === "string") {
757
- $el = that.selectorAll($el);
758
- }
759
- if ($el == null) {
760
- return false;
761
- }
762
- if (CommonUtils.isNodeList($el)) {
763
- let flag = true;
764
- for (let index = 0; index < $el.length; index++) {
765
- const $elItem = $el[index] as HTMLElement;
766
- flag = flag && that.hasClass($elItem, className);
767
- }
768
- return flag;
769
- }
770
- if (!$el?.classList) {
771
- return false;
772
- }
773
- if (!Array.isArray(className)) {
774
- className = className.split(" ");
775
- }
776
- for (let index = 0; index < className.length; index++) {
777
- const item = className[index].trim();
778
- if (!$el.classList.contains(item)) {
779
- return false;
780
- }
781
- }
782
- return true;
783
- }
784
- /**
785
- * 函数在元素内部末尾添加子元素或HTML字符串
786
- * @param $el 目标元素
787
- * @param args 子元素或HTML字符串
788
- * @example
789
- * // 元素a.xx的内部末尾添加一个元素
790
- * DOMUtils.append(document.querySelector("a.xx"), document.querySelector("b.xx"))
791
- * DOMUtils.append("a.xx", "<b class="xx"></b>")
792
- * DOMUtils.append(document, [document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx")])
793
- * DOMUtils.append(document, document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx"))
794
- * */
795
- append(
796
- $el: DOMUtilsTargetElementType | DocumentFragment,
797
- ...args: (
798
- | HTMLElement
799
- | string
800
- | Element
801
- | DocumentFragment
802
- | (HTMLElement | string | Element | DocumentFragment)[]
803
- | NodeList
804
- )[]
805
- ) {
806
- if (typeof $el === "string") {
807
- $el = this.selectorAll($el);
808
- }
809
-
810
- if ($el == null) {
811
- return;
812
- }
813
-
814
- if (CommonUtils.isNodeList($el)) {
815
- // 设置
816
- $el.forEach(($elItem) => {
817
- this.append($elItem as HTMLElement, ...args);
818
- });
819
- return;
820
- }
821
- const handler = ($ele: HTMLElement | DocumentFragment, $target: HTMLElement | string | Node) => {
822
- if ($ele instanceof DocumentFragment) {
823
- if (typeof $target === "string") {
824
- // 字符串转元素
825
- $target = this.toElement($target, true, false);
826
- }
827
- $ele.appendChild($target);
828
- } else {
829
- if (typeof $target === "string") {
830
- $ele.insertAdjacentHTML("beforeend", CommonUtils.createSafeHTML($target));
831
- } else {
832
- $ele.appendChild($target);
833
- }
834
- }
835
- };
836
- const $fragment = this.windowApi.document.createDocumentFragment();
837
- args.forEach((argItem) => {
838
- if (CommonUtils.isNodeList(argItem)) {
839
- // 数组
840
- argItem.forEach((it) => {
841
- handler($fragment, it);
842
- });
843
- } else {
844
- handler($fragment, argItem);
845
- }
846
- });
847
- handler($el, $fragment);
848
- }
849
- /**
850
- * 函数 在元素内部开头添加子元素或HTML字符串
851
- * @param $el 目标元素
852
- * @param args 子元素或HTML字符串
853
- * @example
854
- * // 元素a.xx内部开头添加一个元素
855
- * DOMUtils.prepend(document.querySelector("a.xx"),document.querySelector("b.xx"))
856
- * DOMUtils.prepend("a.xx","'<b class="xx"></b>")
857
- * DOMUtils.prepend(document, [document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx")])
858
- * DOMUtils.prepend(document, document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx"))
859
- * */
860
- prepend(
861
- $el: DOMUtilsTargetElementType | DocumentFragment,
862
- ...args: (
863
- | HTMLElement
864
- | string
865
- | Element
866
- | DocumentFragment
867
- | (HTMLElement | string | Element | DocumentFragment)[]
868
- | NodeList
869
- )[]
870
- ) {
871
- if (typeof $el === "string") {
872
- $el = this.selectorAll($el);
873
- }
874
-
875
- if ($el == null) {
876
- return;
877
- }
878
-
879
- if (CommonUtils.isNodeList($el)) {
880
- // 设置
881
- $el.forEach(($elItem) => {
882
- this.prepend($elItem as HTMLElement, ...args);
883
- });
884
- return;
885
- }
886
- const handler = ($ele: HTMLElement | DocumentFragment, $target: HTMLElement | string | Node) => {
887
- if ($ele instanceof DocumentFragment) {
888
- if (typeof $target === "string") {
889
- // 字符串转元素
890
- $target = this.toElement($target, true, false);
891
- }
892
- $ele.appendChild($target);
893
- } else {
894
- if (typeof $target === "string") {
895
- $ele.insertAdjacentHTML("afterbegin", CommonUtils.createSafeHTML($target));
896
- } else {
897
- const $firstChild = $ele.firstChild;
898
- if ($firstChild) {
899
- $ele.insertBefore($target, $firstChild);
900
- } else {
901
- $ele.prepend($target);
902
- }
903
- }
904
- }
905
- };
906
- const $fragment = this.windowApi.document.createDocumentFragment();
907
- args.forEach((argItem) => {
908
- if (CommonUtils.isNodeList(argItem)) {
909
- // 数组
910
- argItem.forEach((it) => {
911
- handler($fragment, it);
912
- });
913
- } else {
914
- handler($fragment, argItem);
915
- }
916
- });
917
- handler($el, $fragment);
918
- }
919
- /**
920
- * 在元素后面添加兄弟元素或HTML字符串
921
- * @param $el 目标元素
922
- * @param args 兄弟元素或HTML字符串
923
- * @example
924
- * // 元素a.xx后面添加一个元素
925
- * DOMUtils.after(document.querySelector("a.xx"),document.querySelector("b.xx"))
926
- * DOMUtils.after("a.xx","'<b class="xx"></b>")
927
- * DOMUtils.after(document, [document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx")])
928
- * DOMUtils.after(document, document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx"))
929
- * */
930
- after(
931
- $el: DOMUtilsTargetElementType,
932
- ...args: (
933
- | HTMLElement
934
- | string
935
- | Element
936
- | DocumentFragment
937
- | (HTMLElement | string | Element | DocumentFragment)[]
938
- | NodeList
939
- )[]
940
- ) {
941
- if (typeof $el === "string") {
942
- $el = this.selectorAll($el);
943
- }
944
-
945
- if ($el == null) {
946
- return;
947
- }
948
-
949
- if (CommonUtils.isNodeList($el)) {
950
- // 设置
951
- $el.forEach(($elItem) => {
952
- this.after($elItem as HTMLElement, ...args);
953
- });
954
- return;
955
- }
956
- const handler = ($ele: HTMLElement | DocumentFragment, $target: HTMLElement | string | Node) => {
957
- if ($ele instanceof DocumentFragment) {
958
- if (typeof $target === "string") {
959
- // 字符串转元素
960
- $target = this.toElement($target, true, false);
961
- }
962
- $ele.appendChild($target);
963
- } else {
964
- if (typeof $target === "string") {
965
- $ele.insertAdjacentHTML("afterend", CommonUtils.createSafeHTML($target));
966
- } else {
967
- const $parent = $el.parentElement;
968
- const $nextSlibling = $el.nextSibling;
969
- if ($parent && $nextSlibling) {
970
- $parent.insertBefore($target, $nextSlibling);
971
- } else {
972
- $el.after($target);
973
- }
974
- }
975
- }
976
- };
977
- const $fragment = this.windowApi.document.createDocumentFragment();
978
- args.forEach((argItem) => {
979
- if (CommonUtils.isNodeList(argItem)) {
980
- // 数组
981
- argItem.forEach((it) => {
982
- handler($fragment, it);
983
- });
984
- } else {
985
- handler($fragment, argItem);
986
- }
987
- });
988
- handler($el, $fragment);
989
- }
990
- /**
991
- * 在元素前面添加兄弟元素或HTML字符串
992
- * @param $el 目标元素
993
- * @param args 兄弟元素或HTML字符串
994
- * @example
995
- * // 元素a.xx前面添加一个元素
996
- * DOMUtils.before(document.querySelector("a.xx"),document.querySelector("b.xx"))
997
- * DOMUtils.before("a.xx","'<b class="xx"></b>")
998
- * DOMUtils.before(document, [document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx")])
999
- * DOMUtils.before(document, document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx"))
1000
- *
1001
- * */
1002
- before(
1003
- $el: DOMUtilsTargetElementType,
1004
- ...args: (
1005
- | HTMLElement
1006
- | string
1007
- | Element
1008
- | DocumentFragment
1009
- | (HTMLElement | string | Element | DocumentFragment)[]
1010
- | NodeList
1011
- )[]
1012
- ) {
1013
- if (typeof $el === "string") {
1014
- $el = this.selectorAll($el);
1015
- }
1016
-
1017
- if ($el == null) {
1018
- return;
1019
- }
1020
-
1021
- if (CommonUtils.isNodeList($el)) {
1022
- // 设置
1023
- $el.forEach(($elItem) => {
1024
- this.before($elItem as HTMLElement, ...args);
1025
- });
1026
- return;
1027
- }
1028
- const handler = ($ele: HTMLElement | DocumentFragment, $target: HTMLElement | string | Node) => {
1029
- if ($ele instanceof DocumentFragment) {
1030
- if (typeof $target === "string") {
1031
- // 字符串转元素
1032
- $target = this.toElement($target, true, false);
1033
- }
1034
- $ele.appendChild($target);
1035
- } else {
1036
- if (typeof $target === "string") {
1037
- $el.insertAdjacentHTML("beforebegin", CommonUtils.createSafeHTML($target));
1038
- } else {
1039
- const $parent = $el.parentElement;
1040
- if ($parent) {
1041
- $parent.insertBefore($target, $el);
1042
- } else {
1043
- $el.before($target);
1044
- }
1045
- }
1046
- }
1047
- };
1048
- const $fragment = this.windowApi.document.createDocumentFragment();
1049
- args.forEach((argItem) => {
1050
- if (CommonUtils.isNodeList(argItem)) {
1051
- // 数组
1052
- argItem.forEach((it) => {
1053
- handler($fragment, it);
1054
- });
1055
- } else {
1056
- handler($fragment, argItem);
1057
- }
1058
- });
1059
- handler($el, $fragment);
1060
- }
1061
- /**
1062
- * 移除元素
1063
- * @param $el 目标元素,可以是数组、单个元素、NodeList、元素选择器
1064
- * @example
1065
- * DOMUtils.remove(document.querySelector("a.xx"))
1066
- * DOMUtils.remove(document.querySelectorAll("a.xx"))
1067
- * DOMUtils.remove("a.xx")
1068
- * DOMUtils.remove([a.xxx, div.xxx, span.xxx])
1069
- * */
1070
- remove($el: DOMUtilsTargetElementType | Element) {
1071
- const that = this;
1072
- if (typeof $el === "string") {
1073
- $el = that.selectorAll($el);
1074
- }
1075
- if ($el == null) {
1076
- return;
1077
- }
1078
- if (CommonUtils.isNodeList($el)) {
1079
- $el.forEach(($elItem) => {
1080
- that.remove($elItem as HTMLElement);
1081
- });
1082
- return;
1083
- }
1084
- if (typeof $el.remove === "function") {
1085
- $el.remove();
1086
- } else if ($el.parentElement) {
1087
- $el.parentElement.removeChild($el);
1088
- } else if ($el.parentNode) {
1089
- $el.parentNode.removeChild($el);
1090
- }
1091
- }
1092
- /**
1093
- * 移除元素内所有的子元素
1094
- * @param $el 目标元素
1095
- * @example
1096
- * // 移除元素a.xx元素的所有子元素
1097
- * DOMUtils.empty(document.querySelector("a.xx"))
1098
- * DOMUtils.empty("a.xx")
1099
- * */
1100
- empty($el: DOMUtilsTargetElementType | Element) {
1101
- const that = this;
1102
- if (typeof $el === "string") {
1103
- $el = that.selectorAll($el);
1104
- }
1105
- if ($el == null) {
1106
- return;
1107
- }
1108
- if (CommonUtils.isNodeList($el)) {
1109
- // 设置
1110
- $el.forEach(($elItem) => {
1111
- that.empty($elItem as HTMLElement);
1112
- });
1113
- return;
1114
- }
1115
- if ($el.innerHTML) {
1116
- $el.innerHTML = "";
1117
- } else if ($el.textContent) {
1118
- $el.textContent = "";
1119
- }
1120
- }
1121
- /**
1122
- * 获取元素相对于文档的偏移坐标(加上文档的滚动条)
1123
- * @param $el 目标元素
1124
- * @example
1125
- * // 获取元素a.xx的对于文档的偏移坐标
1126
- * DOMUtils.offset(document.querySelector("a.xx"))
1127
- * DOMUtils.offset("a.xx")
1128
- * > 0
1129
- */
1130
- offset($el: HTMLElement | string) {
1131
- const that = this;
1132
- if (typeof $el === "string") {
1133
- $el = that.selector($el) as HTMLElement;
1134
- }
1135
- if ($el == null) {
1136
- return;
1137
- }
1138
-
1139
- const rect = $el.getBoundingClientRect();
1140
- return {
1141
- /** y轴偏移 */
1142
- top: rect.top + that.windowApi.globalThis.scrollY,
1143
- /** x轴偏移 */
1144
- left: rect.left + that.windowApi.globalThis.scrollX,
1145
- };
1146
- }
1147
- /**
1148
- * 获取元素的宽度
1149
- * @param $el 要获取宽度的元素
1150
- * @param value 宽度值
1151
- * @param isShow 是否已进行isShow,避免爆堆栈
1152
- * @returns 元素的宽度,单位为像素
1153
- * @example
1154
- * // 获取元素a.xx的宽度
1155
- * DOMUtils.width(document.querySelector("a.xx"))
1156
- * DOMUtils.width("a.xx")
1157
- * > 100
1158
- * // 获取window的宽度
1159
- * DOMUtils.width(window)
1160
- * > 400
1161
- * @example
1162
- * // 设置元素a.xx的宽度为200
1163
- * DOMUtils.width(document.querySelector("a.xx"),200)
1164
- * DOMUtils.width("a.xx",200)
1165
- */
1166
- width($el: HTMLElement | string | Window | typeof globalThis | Document, isShow?: boolean): number;
1167
- width($el: HTMLElement | string | Window | typeof globalThis | Document, isShow: boolean = false): number {
1168
- const that = this;
1169
- if (typeof $el === "string") {
1170
- $el = that.selector<HTMLElement>($el)!;
1171
- }
1172
- if (CommonUtils.isWin($el)) {
1173
- return that.windowApi.window.document.documentElement.clientWidth;
1174
- }
1175
- if (($el as HTMLElement).nodeType === 9) {
1176
- /* Document文档节点 */
1177
- $el = $el as Document;
1178
- return Math.max(
1179
- $el.body.scrollWidth,
1180
- $el.documentElement.scrollWidth,
1181
- $el.body.offsetWidth,
1182
- $el.documentElement.offsetWidth,
1183
- $el.documentElement.clientWidth
1184
- );
1185
- }
1186
- if (isShow || (!isShow && CommonUtils.isShow($el as HTMLElement))) {
1187
- /* 已显示 */
1188
- /* 不从style中获取对应的宽度,因为可能使用了class定义了width !important */
1189
- $el = $el as HTMLElement;
1190
- /* 如果element.style.width为空 则从css里面获取是否定义了width信息如果定义了 则读取css里面定义的宽度width */
1191
- if (parseFloat(CommonUtils.getStyleValue($el, "width").toString()) > 0) {
1192
- return parseFloat(CommonUtils.getStyleValue($el, "width").toString());
1193
- }
1194
-
1195
- /* 如果从css里获取到的值不是大于0 可能是auto 则通过offsetWidth来进行计算 */
1196
- if ($el.offsetWidth > 0) {
1197
- const borderLeftWidth = CommonUtils.getStyleValue($el, "borderLeftWidth");
1198
- const borderRightWidth = CommonUtils.getStyleValue($el, "borderRightWidth");
1199
- const paddingLeft = CommonUtils.getStyleValue($el, "paddingLeft");
1200
- const paddingRight = CommonUtils.getStyleValue($el, "paddingRight");
1201
- const backHeight =
1202
- parseFloat($el.offsetWidth.toString()) -
1203
- parseFloat(borderLeftWidth.toString()) -
1204
- parseFloat(borderRightWidth.toString()) -
1205
- parseFloat(paddingLeft.toString()) -
1206
- parseFloat(paddingRight.toString());
1207
- return parseFloat(backHeight.toString());
1208
- }
1209
- return 0;
1210
- } else {
1211
- /* 未显示 */
1212
- $el = $el as HTMLElement;
1213
- const { recovery } = CommonUtils.forceShow($el);
1214
- const width = that.width($el, true);
1215
- recovery();
1216
- return width;
1217
- }
1218
- }
1219
-
1220
- /**
1221
- * 获取元素的高度
1222
- * @param $el 要获取高度的元素
1223
- * @param isShow 是否已进行isShow,避免爆堆栈
1224
- * @returns 元素的高度,单位为像素
1225
- * @example
1226
- * // 获取元素a.xx的高度
1227
- * DOMUtils.height(document.querySelector("a.xx"))
1228
- * DOMUtils.height("a.xx")
1229
- * > 100
1230
- * // 获取window的高度
1231
- * DOMUtils.height(window)
1232
- * > 700
1233
- * @example
1234
- * // 设置元素a.xx的高度为200
1235
- * DOMUtils.height(document.querySelector("a.xx"),200)
1236
- * DOMUtils.height("a.xx",200)
1237
- */
1238
- height($el: HTMLElement | string | Window | typeof globalThis | Document, isShow?: boolean): number;
1239
- height($el: HTMLElement | string | Window | typeof globalThis | Document, isShow: boolean = false): number {
1240
- const that = this;
1241
- if (CommonUtils.isWin($el)) {
1242
- return that.windowApi.window.document.documentElement.clientHeight;
1243
- }
1244
- if (typeof $el === "string") {
1245
- $el = that.selector($el) as HTMLElement;
1246
- }
1247
- if (($el as Document).nodeType === 9) {
1248
- $el = $el as Document;
1249
- /* Document文档节点 */
1250
- return Math.max(
1251
- $el.body.scrollHeight,
1252
- $el.documentElement.scrollHeight,
1253
- $el.body.offsetHeight,
1254
- $el.documentElement.offsetHeight,
1255
- $el.documentElement.clientHeight
1256
- );
1257
- }
1258
- if (isShow || (!isShow && CommonUtils.isShow($el as HTMLElement))) {
1259
- $el = $el as HTMLElement;
1260
- /* 已显示 */
1261
- /* 从style中获取对应的高度,因为可能使用了class定义了width !important */
1262
- /* 如果element.style.height为空 则从css里面获取是否定义了height信息如果定义了 则读取css里面定义的高度height */
1263
- if (parseFloat(CommonUtils.getStyleValue($el, "height").toString()) > 0) {
1264
- return parseFloat(CommonUtils.getStyleValue($el, "height").toString());
1265
- }
1266
-
1267
- /* 如果从css里获取到的值不是大于0 可能是auto 则通过offsetHeight来进行计算 */
1268
- if ($el.offsetHeight > 0) {
1269
- const borderTopWidth = CommonUtils.getStyleValue($el, "borderTopWidth");
1270
- const borderBottomWidth = CommonUtils.getStyleValue($el, "borderBottomWidth");
1271
- const paddingTop = CommonUtils.getStyleValue($el, "paddingTop");
1272
- const paddingBottom = CommonUtils.getStyleValue($el, "paddingBottom");
1273
- const backHeight =
1274
- parseFloat($el.offsetHeight.toString()) -
1275
- parseFloat(borderTopWidth.toString()) -
1276
- parseFloat(borderBottomWidth.toString()) -
1277
- parseFloat(paddingTop.toString()) -
1278
- parseFloat(paddingBottom.toString());
1279
- return parseFloat(backHeight.toString());
1280
- }
1281
- return 0;
1282
- } else {
1283
- /* 未显示 */
1284
- $el = $el as HTMLElement;
1285
- const { recovery } = CommonUtils.forceShow($el);
1286
- const height = that.height($el, true);
1287
- recovery();
1288
- return height;
1289
- }
1290
- }
1291
- /**
1292
- * 获取元素的外部宽度(包括边框和外边距)
1293
- * @param $el 要获取外部宽度的元素
1294
- * @param [isShow=false] 是否已进行isShow,避免爆堆栈
1295
- * @returns 元素的外部宽度,单位为像素
1296
- * @example
1297
- * // 获取元素a.xx的外部宽度
1298
- * DOMUtils.outerWidth(document.querySelector("a.xx"))
1299
- * DOMUtils.outerWidth("a.xx")
1300
- * > 100
1301
- * // 获取window的外部宽度
1302
- * DOMUtils.outerWidth(window)
1303
- * > 400
1304
- */
1305
- outerWidth($el: HTMLElement | string | Window | typeof globalThis | Document, isShow?: boolean): number;
1306
- outerWidth($el: HTMLElement | string | Window | typeof globalThis | Document, isShow: boolean = false): number {
1307
- const that = this;
1308
- if (CommonUtils.isWin($el)) {
1309
- return that.windowApi.window.innerWidth;
1310
- }
1311
- if (typeof $el === "string") {
1312
- $el = that.selector($el) as HTMLElement;
1313
- }
1314
- $el = $el as HTMLElement;
1315
- if (isShow || (!isShow && CommonUtils.isShow($el))) {
1316
- const style = that.windowApi.globalThis.getComputedStyle($el, null);
1317
- const marginLeft = CommonUtils.getStyleValue(style, "marginLeft");
1318
- const marginRight = CommonUtils.getStyleValue(style, "marginRight");
1319
- return $el.offsetWidth + marginLeft + marginRight;
1320
- } else {
1321
- const { recovery } = CommonUtils.forceShow($el);
1322
- const outerWidth = that.outerWidth($el, true);
1323
- recovery();
1324
- return outerWidth;
1325
- }
1326
- }
1327
- /**
1328
- * 获取元素的外部高度(包括边框和外边距)
1329
- * @param {HTMLElement|string} $el 要获取外部高度的元素
1330
- * @param {boolean} [isShow=false] 是否已进行isShow,避免爆堆栈
1331
- * @returns {number} 元素的外部高度,单位为像素
1332
- * @example
1333
- * // 获取元素a.xx的外部高度
1334
- * DOMUtils.outerHeight(document.querySelector("a.xx"))
1335
- * DOMUtils.outerHeight("a.xx")
1336
- * > 100
1337
- * // 获取window的外部高度
1338
- * DOMUtils.outerHeight(window)
1339
- * > 700
1340
- */
1341
- outerHeight($el: HTMLElement | string | Window | typeof globalThis | Document, isShow?: boolean): number;
1342
- outerHeight($el: HTMLElement | string | Window | typeof globalThis | Document, isShow: boolean = false): number {
1343
- const that = this;
1344
- if (CommonUtils.isWin($el)) {
1345
- return that.windowApi.window.innerHeight;
1346
- }
1347
- if (typeof $el === "string") {
1348
- $el = that.selector($el) as HTMLElement;
1349
- }
1350
- $el = $el as HTMLElement;
1351
- if (isShow || (!isShow && CommonUtils.isShow($el))) {
1352
- const style = that.windowApi.globalThis.getComputedStyle($el, null);
1353
- const marginTop = CommonUtils.getStyleValue(style, "marginTop");
1354
- const marginBottom = CommonUtils.getStyleValue(style, "marginBottom");
1355
- return $el.offsetHeight + marginTop + marginBottom;
1356
- } else {
1357
- const { recovery } = CommonUtils.forceShow($el);
1358
- const outerHeight = that.outerHeight($el, true);
1359
- recovery();
1360
- return outerHeight;
1361
- }
1362
- }
1363
- /**
1364
- * 将一个元素替换为另一个元素
1365
- * @param $el 目标元素
1366
- * @param $newEl 新元素
1367
- * @example
1368
- * // 替换元素a.xx为b.xx
1369
- * DOMUtils.replaceWith(document.querySelector("a.xx"),document.querySelector("b.xx"))
1370
- * DOMUtils.replaceWith("a.xx",'<b class="xx"></b>')
1371
- */
1372
- replaceWith($el: DOMUtilsTargetElementType, $newEl: HTMLElement | string | Node) {
1373
- const that = this;
1374
- if (typeof $el === "string") {
1375
- $el = that.selectorAll($el);
1376
- }
1377
- if ($el == null) {
1378
- return;
1379
- }
1380
- if (CommonUtils.isNodeList($el)) {
1381
- // 设置
1382
- $el.forEach(($elItem) => {
1383
- that.replaceWith($elItem as HTMLElement, $newEl);
1384
- });
1385
- return;
1386
- }
1387
- if (typeof $newEl === "string") {
1388
- $newEl = that.toElement($newEl, false, false);
1389
- }
1390
- const $parent = $el.parentElement;
1391
- if ($parent) {
1392
- $parent.replaceChild($newEl as Node, $el);
1393
- } else {
1394
- that.after($el, $newEl as HTMLElement);
1395
- $el.remove();
1396
- }
1397
- }
1398
- /**
1399
- * 将一个元素包裹在指定的HTML元素中
1400
- * @param $el 要包裹的元素
1401
- * @param wrapperHTML 要包裹的HTML元素的字符串表示形式
1402
- * @example
1403
- * // 将a.xx元素外面包裹一层div
1404
- * DOMUtils.wrap(document.querySelector("a.xx"),"<div></div>")
1405
- */
1406
- wrap($el: DOMUtilsTargetElementType, wrapperHTML: string) {
1407
- const that = this;
1408
- if (typeof $el === "string") {
1409
- $el = that.selectorAll($el);
1410
- }
1411
- if ($el == null) {
1412
- return;
1413
- }
1414
- if (CommonUtils.isNodeList($el)) {
1415
- // 设置
1416
- $el.forEach(($elItem) => {
1417
- that.wrap($elItem as HTMLElement, wrapperHTML);
1418
- });
1419
- return;
1420
- }
1421
- $el = $el as HTMLElement;
1422
- // 创建一个新的div元素,并将wrapperHTML作为其innerHTML
1423
- const $wrapper = that.windowApi.document.createElement("div");
1424
- that.html($wrapper, wrapperHTML);
1425
-
1426
- const wrapperFirstChild = $wrapper.firstChild as HTMLElement;
1427
- // 将要包裹的元素插入目标元素前面
1428
- const parentElement = $el.parentElement as HTMLElement;
1429
- parentElement.insertBefore(wrapperFirstChild, $el);
1430
-
1431
- // 将要包裹的元素移动到wrapper中
1432
- wrapperFirstChild.appendChild($el);
1433
- }
1434
- /**
1435
- * 获取当前元素的前一个兄弟元素
1436
- * @param $el 当前元素
1437
- * @returns 前一个兄弟元素
1438
- * @example
1439
- * // 获取a.xx元素前一个兄弟元素
1440
- * DOMUtils.prev(document.querySelector("a.xx"))
1441
- * DOMUtils.prev("a.xx")
1442
- * > <div ...>....</div>
1443
- */
1444
- prev($el: HTMLElement | string): HTMLElement;
1445
- prev($el: HTMLElement | string) {
1446
- const that = this;
1447
- if (typeof $el === "string") {
1448
- $el = that.selector($el) as HTMLElement;
1449
- }
1450
- if ($el == null) {
1451
- return;
1452
- }
1453
- return $el.previousElementSibling as HTMLElement;
1454
- }
1455
- /**
1456
- * 获取当前元素的后一个兄弟元素
1457
- * @param $el 当前元素
1458
- * @returns 后一个兄弟元素
1459
- * @example
1460
- * // 获取a.xx元素前一个兄弟元素
1461
- * DOMUtils.next(document.querySelector("a.xx"))
1462
- * DOMUtils.next("a.xx")
1463
- * > <div ...>....</div>
1464
- */
1465
- next($el: HTMLElement | string): HTMLElement;
1466
- next($el: HTMLElement | string) {
1467
- const that = this;
1468
- if (typeof $el === "string") {
1469
- $el = that.selector($el) as HTMLElement;
1470
- }
1471
- if ($el == null) {
1472
- return;
1473
- }
1474
- return $el.nextElementSibling as HTMLElement;
1475
- }
1476
- /**
1477
- * 获取当前元素的所有兄弟元素
1478
- * @param element 当前元素
1479
- * @returns 所有兄弟元素
1480
- * @example
1481
- * // 获取a.xx元素所有兄弟元素
1482
- * DOMUtils.siblings(document.querySelector("a.xx"))
1483
- * DOMUtils.siblings("a.xx")
1484
- * > (3)[div.logo-wrapper, div.forum-block, div.more-btn-desc]
1485
- */
1486
- siblings(element: HTMLElement | string): HTMLElement[];
1487
- siblings($el: HTMLElement | string) {
1488
- const that = this;
1489
- if (typeof $el === "string") {
1490
- $el = that.selector($el) as HTMLElement;
1491
- }
1492
- if ($el == null) {
1493
- return;
1494
- }
1495
- return Array.from(($el.parentElement as HTMLElement).children as HTMLCollectionOf<HTMLElement>).filter(
1496
- ($child) => $child !== $el
1497
- );
1498
- }
1499
- /**
1500
- * 获取当前元素的父元素
1501
- * @param $el 当前元素
1502
- * @returns 父元素
1503
- * @example
1504
- * // 获取a.xx元素的父元素
1505
- * DOMUtils.parent(document.querySelector("a.xx"))
1506
- * DOMUtils.parent("a.xx")
1507
- * > <div ...>....</div>
1508
- */
1509
- parent($el: HTMLElement | string): HTMLElement;
1510
- /**
1511
- * 获取当前元素的父元素
1512
- * @param $el 当前元素
1513
- * @returns 父元素
1514
- * @example
1515
- * // 获取a.xx元素的父元素
1516
- * DOMUtils.parent(document.querySelector("a.xx"))
1517
- * DOMUtils.parent("a.xx")
1518
- * > <div ...>....</div>
1519
- */
1520
- parent($el: HTMLElement[] | NodeList): HTMLElement[];
1521
- /**
1522
- * 获取当前元素的父元素
1523
- * @param $el 当前元素
1524
- * @returns 父元素
1525
- * @example
1526
- * // 获取a.xx元素的父元素
1527
- * DOMUtils.parent(document.querySelector("a.xx"))
1528
- * DOMUtils.parent("a.xx")
1529
- * > <div ...>....</div>
1530
- */
1531
- parent($el: HTMLElement | Element | Node | NodeList | string | HTMLElement[]) {
1532
- const that = this;
1533
- if (typeof $el === "string") {
1534
- $el = that.selector<HTMLElement>($el)!;
1535
- }
1536
- if ($el == null) {
1537
- return;
1538
- }
1539
- if (CommonUtils.isNodeList($el)) {
1540
- const resultArray: HTMLElement[] = [];
1541
- $el.forEach(($elItem) => {
1542
- resultArray.push(that.parent($elItem as HTMLElement));
1543
- });
1544
- return resultArray;
1545
- } else {
1546
- return $el.parentElement;
1547
- }
1548
- }
1549
- /**
1550
- * 将字符串转为Element元素
1551
- * @param html
1552
- * @param useParser 是否使用DOMParser来生成元素,有些时候通过DOMParser生成的元素有点问题
1553
- * + true 使用DOMPraser来转换字符串
1554
- * + false (默认)创建一个div,里面放入字符串,然后提取firstChild
1555
- * @param isComplete 是否是完整的
1556
- * + true 如果useParser为true,那么返回整个使用DOMParser转换成的Document
1557
- * 如果useParser为false,返回一个DIV元素,DIV元素内包裹着需要转换的字符串
1558
- * + false (默认)如果useParser为true,那么返回整个使用DOMParser转换成的Document的body
1559
- * 如果useParser为false,返回一个DIV元素的firstChild
1560
- * @example
1561
- * // 将字符串转为Element元素
1562
- * DOMUtils.toElement("<a href='xxxx'></a>")
1563
- * > <a href="xxxx"></a>
1564
- * @example
1565
- * // 使用DOMParser将字符串转为Element元素
1566
- * DOMUtils.toElement("<a href='xxxx'></a>",true)
1567
- * > <a href="xxxx"></a>
1568
- * @example
1569
- * // 由于需要转换的元素是多个元素,将字符串转为完整的Element元素
1570
- * DOMUtils.toElement("<a href='xxxx'></a><a href='xxxx'></a>",false, true)
1571
- * > <div><a href="xxxx"></a><a href='xxxx'></a></div>
1572
- * @example
1573
- * // 由于需要转换的元素是多个元素,使用DOMParser将字符串转为完整的Element元素
1574
- * DOMUtils.toElement("<a href='xxxx'></a><a href='xxxx'></a>",true, true)
1575
- * > #document
1576
- */
1577
- toElement<T1 extends boolean, T2 extends boolean>(
1578
- html: string,
1579
- useParser?: T1,
1580
- isComplete?: T2
1581
- ): T1 extends true ? (T2 extends true ? Document : HTMLElement) : HTMLElement;
1582
- toElement(html: string, useParser = false, isComplete = false) {
1583
- const that = this;
1584
- // 去除html前后的空格
1585
- html = html.trim();
1586
- function parseHTMLByDOMParser() {
1587
- const parser = new DOMParser();
1588
- if (isComplete) {
1589
- return parser.parseFromString(html, "text/html");
1590
- } else {
1591
- return parser.parseFromString(html, "text/html").body.firstChild;
1592
- }
1593
- }
1594
- function parseHTMLByCreateDom() {
1595
- const $el = that.windowApi.document.createElement("div");
1596
- that.html($el, html);
1597
- if (isComplete) {
1598
- return $el;
1599
- } else {
1600
- return $el.firstElementChild ?? $el.firstChild;
1601
- }
1602
- }
1603
- if (useParser) {
1604
- return parseHTMLByDOMParser();
1605
- } else {
1606
- return parseHTMLByCreateDom();
1607
- }
1608
- }
1609
- /**
1610
- * 将字符串转为Element元素数组
1611
- * @param html
1612
- * @param useParser 是否使用DOMParser来生成元素,有些时候通过DOMParser生成的元素有点问题
1613
- * + true 使用DOMPraser来转换字符串
1614
- * + false (默认)创建一个div,里面放入字符串,然后提取childNodes
1615
- * @example
1616
- * // 将字符串转为Element元素数组
1617
- * DOMUtils.toElements("<a href='xxxx'></a>")
1618
- * > [<a href="xxxx"></a>]
1619
- * @example
1620
- * // 使用DOMParser将字符串转为Element元素数组
1621
- * DOMUtils.toElements("<a href='xxxx'></a>",true)
1622
- * > [<a href="xxxx"></a>]
1623
- */
1624
- toElements(html: string, useParser = false) {
1625
- const that = this;
1626
- // 去除html前后的空格
1627
- html = html.trim();
1628
- function parseHTMLByDOMParser() {
1629
- const parser = new DOMParser();
1630
- return Array.from(parser.parseFromString(html, "text/html").body.childNodes);
1631
- }
1632
- function parseHTMLByCreateDom() {
1633
- const $el = that.windowApi.document.createElement("div");
1634
- that.html($el, html);
1635
- return Array.from($el.childNodes);
1636
- }
1637
- if (useParser) {
1638
- return parseHTMLByDOMParser();
1639
- } else {
1640
- return parseHTMLByCreateDom();
1641
- }
1642
- }
1643
- /**
1644
- * 序列化表单元素
1645
- * @param $form 表单元素
1646
- * @example
1647
- * DOMUtils.serialize(document.querySelector("form"))
1648
- * > xxx=xxx&aaa=
1649
- */
1650
- serialize($form: HTMLFormElement): string {
1651
- if (!($form instanceof HTMLFormElement)) {
1652
- throw new TypeError("DOMUtils.serialize 参数必须是HTMLFormElement");
1653
- }
1654
- const elements = $form.elements;
1655
- const serializedArray: { name: string; value: string }[] = [];
1656
-
1657
- for (let index = 0; index < elements.length; index++) {
1658
- const $el = elements[index] as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
1659
-
1660
- if (
1661
- $el.name &&
1662
- !$el.disabled &&
1663
- (($el as HTMLInputElement).checked ||
1664
- ["text", "hidden", "password", "textarea", "select-one", "select-multiple"].includes($el.type))
1665
- ) {
1666
- if ($el.type === "select-multiple") {
1667
- for (let j = 0; j < ($el as HTMLSelectElement).options.length; j++) {
1668
- if (($el as HTMLSelectElement).options[j].selected) {
1669
- serializedArray.push({
1670
- name: ($el as HTMLSelectElement).name,
1671
- value: ($el as HTMLSelectElement).options[j].value,
1672
- });
1673
- }
1674
- }
1675
- } else {
1676
- serializedArray.push({ name: $el.name, value: $el.value });
1677
- }
1678
- }
1679
- }
1680
-
1681
- return serializedArray
1682
- .map((item) => `${encodeURIComponent(item.name)}=${encodeURIComponent(item.value)}`)
1683
- .join("&");
1684
- }
1685
- /**
1686
- * 创建一个新的DOMUtils实例
1687
- * @param option
1688
- * @returns
1689
- */
1690
- createDOMUtils(option?: WindowApiOption) {
1691
- return new DOMUtils(option);
1692
- }
1693
- /**
1694
- * 获取文字的位置信息
1695
- * @param $input 输入框
1696
- * @param selectionStart 起始位置
1697
- * @param selectionEnd 结束位置
1698
- * @example
1699
- * DOMUtils.getTextBoundingRect(document.querySelector("input"));
1700
- */
1701
- getTextBoundingRect(
1702
- $input: HTMLInputElement,
1703
- selectionStart?: number | string,
1704
- selectionEnd?: number | string
1705
- ): DOMRect {
1706
- const that = this;
1707
- // Basic parameter validation
1708
- if (!$input || !("value" in $input)) return $input;
1709
- if (selectionStart == null) {
1710
- selectionStart = $input.selectionStart || 0;
1711
- }
1712
- if (selectionEnd == null) {
1713
- selectionEnd = $input.selectionEnd || 0;
1714
- }
1715
- if (typeof selectionStart == "string") selectionStart = parseFloat(selectionStart);
1716
- if (typeof selectionStart != "number" || isNaN(selectionStart)) {
1717
- selectionStart = 0;
1718
- }
1719
- if (selectionStart < 0) selectionStart = 0;
1720
- else selectionStart = Math.min($input.value.length, selectionStart);
1721
- if (typeof selectionEnd == "string") selectionEnd = parseFloat(selectionEnd);
1722
- if (typeof selectionEnd != "number" || isNaN(selectionEnd) || selectionEnd < selectionStart) {
1723
- selectionEnd = selectionStart;
1724
- }
1725
- if (selectionEnd < 0) selectionEnd = 0;
1726
- else selectionEnd = Math.min($input.value.length, selectionEnd);
1727
-
1728
- // If available (thus IE), use the createTextRange method
1729
- if (typeof (<any>$input).createTextRange == "function") {
1730
- const range = ($input as any).createTextRange();
1731
- range.collapse(true);
1732
- range.moveStart("character", selectionStart);
1733
- range.moveEnd("character", selectionEnd - selectionStart);
1734
- return range.getBoundingClientRect();
1735
- }
1736
- // createTextRange is not supported, create a fake text range
1737
- const offset = getInputOffset(),
1738
- width = getInputCSS("width", true),
1739
- height = getInputCSS("height", true);
1740
- let topPos = offset.top;
1741
- let leftPos = offset.left;
1742
-
1743
- // Styles to simulate a node in an input field
1744
- let cssDefaultStyles = "white-space:pre;padding:0;margin:0;";
1745
- const listOfModifiers = [
1746
- "direction",
1747
- "font-family",
1748
- "font-size",
1749
- "font-size-adjust",
1750
- "font-variant",
1751
- "font-weight",
1752
- "font-style",
1753
- "letter-spacing",
1754
- "line-height",
1755
- "text-align",
1756
- "text-indent",
1757
- "text-transform",
1758
- "word-wrap",
1759
- "word-spacing",
1760
- ];
1761
- topPos += getInputCSS("padding-top", true) as number;
1762
- topPos += getInputCSS("border-top-width", true) as number;
1763
- leftPos += getInputCSS("padding-left", true) as number;
1764
- leftPos += getInputCSS("border-left-width", true) as number;
1765
- leftPos += 1; //Seems to be necessary
1766
-
1767
- for (let index = 0; index < listOfModifiers.length; index++) {
1768
- const property = listOfModifiers[index];
1769
- cssDefaultStyles += property + ":" + getInputCSS(property, false) + ";";
1770
- }
1771
- // End of CSS variable checks
1772
- // 不能为空,不然获取不到高度
1773
- const text = $input.value || "G",
1774
- textLen = text.length,
1775
- fakeClone = that.windowApi.document.createElement("div");
1776
- if (selectionStart > 0) appendPart(0, selectionStart);
1777
- const fakeRange = appendPart(selectionStart, selectionEnd);
1778
- if (textLen > selectionEnd) appendPart(selectionEnd, textLen);
1779
-
1780
- // Styles to inherit the font styles of the element
1781
- fakeClone.style.cssText = cssDefaultStyles;
1782
-
1783
- // Styles to position the text node at the desired position
1784
- fakeClone.style.position = "absolute";
1785
- fakeClone.style.top = topPos + "px";
1786
- fakeClone.style.left = leftPos + "px";
1787
- fakeClone.style.width = width + "px";
1788
- fakeClone.style.height = height + "px";
1789
- that.windowApi.document.body.appendChild(fakeClone);
1790
- const returnValue = fakeRange.getBoundingClientRect(); //Get rect
1791
-
1792
- fakeClone?.parentNode?.removeChild(fakeClone); //Remove temp
1793
- return returnValue;
1794
-
1795
- // Local functions for readability of the previous code
1796
- /**
1797
- *
1798
- * @param start
1799
- * @param end
1800
- * @returns
1801
- */
1802
- function appendPart(start: number, end: number) {
1803
- const span = that.windowApi.document.createElement("span");
1804
- span.style.cssText = cssDefaultStyles; //Force styles to prevent unexpected results
1805
- span.textContent = text.substring(start, end);
1806
- fakeClone.appendChild(span);
1807
- return span;
1808
- }
1809
- // Computing offset position
1810
- function getInputOffset() {
1811
- const body = that.windowApi.document.body,
1812
- win = that.windowApi.document.defaultView!,
1813
- docElem = that.windowApi.document.documentElement,
1814
- $box = that.windowApi.document.createElement("div");
1815
- $box.style.paddingLeft = $box.style.width = "1px";
1816
- body.appendChild($box);
1817
- const isBoxModel = $box.offsetWidth == 2;
1818
- body.removeChild($box);
1819
- const $boxRect = $input.getBoundingClientRect();
1820
- const clientTop = docElem.clientTop || body.clientTop || 0,
1821
- clientLeft = docElem.clientLeft || body.clientLeft || 0,
1822
- scrollTop = win.pageYOffset || (isBoxModel && docElem.scrollTop) || body.scrollTop,
1823
- scrollLeft = win.pageXOffset || (isBoxModel && docElem.scrollLeft) || body.scrollLeft;
1824
- return {
1825
- top: $boxRect.top + scrollTop - clientTop,
1826
- left: $boxRect.left + scrollLeft - clientLeft,
1827
- };
1828
- }
1829
- /**
1830
- *
1831
- * @param prop
1832
- * @param isNumber
1833
- * @returns
1834
- */
1835
- function getInputCSS<T extends boolean>(prop: string, isNumber: T): T extends true ? number : string {
1836
- const val = that.windowApi.document.defaultView!.getComputedStyle($input, null).getPropertyValue(prop);
1837
- if (isNumber) {
1838
- return parseFloat(val) as T extends true ? number : string;
1839
- } else {
1840
- return val as T extends true ? number : string;
1841
- }
1842
- }
1843
- }
1844
- /**
1845
- * 在页面中增加style元素,如果html节点存在子节点,添加子节点第一个,反之,添加到html节点的子节点最后一个
1846
- * @param cssText css字符串
1847
- * @returns 返回添加的CSS标签
1848
- * @example
1849
- * DOMUtils.addStyle("html{}");
1850
- * > <style type="text/css">html{}</style>
1851
- */
1852
- addStyle(cssText: string): HTMLStyleElement;
1853
- addStyle(cssText: string): HTMLStyleElement {
1854
- if (typeof cssText !== "string") {
1855
- throw new Error("DOMUtils.addStyle 参数cssText 必须为String类型");
1856
- }
1857
- const $css = this.createElement("style", {
1858
- type: "text/css",
1859
- innerHTML: cssText,
1860
- });
1861
- if (this.windowApi.document.head) {
1862
- /* 插入head最后 */
1863
- this.windowApi.document.head.appendChild($css);
1864
- } else if (this.windowApi.document.documentElement.childNodes.length === 0) {
1865
- /* 插入#html后 */
1866
- this.windowApi.document.documentElement.appendChild($css);
1867
- } else {
1868
- /* 插入#html第一个元素前 */
1869
- this.windowApi.document.documentElement.insertBefore($css, this.windowApi.document.documentElement.childNodes[0]);
1870
- }
1871
- return $css;
1872
- }
1873
- /**
1874
- * 检测点击的地方是否在该元素区域内
1875
- * @param $el 需要检测的元素
1876
- * @returns
1877
- * + true 点击在元素上
1878
- * + false 未点击在元素上
1879
- * @example
1880
- * DOMUtils.checkUserClickInNode(document.querySelector(".xxx"));
1881
- * > false
1882
- **/
1883
- checkUserClickInNode($el: Element | Node | HTMLElement) {
1884
- const that = this;
1885
- if (!CommonUtils.isDOM($el)) {
1886
- throw new Error("DOMUtils.checkUserClickInNode 参数 targetNode 必须为 Element|Node 类型");
1887
- }
1888
- const clickEvent = that.windowApi.window.event as PointerEvent;
1889
- const touchEvent = that.windowApi.window.event as TouchEvent;
1890
- const $click = clickEvent?.composedPath()?.[0] as HTMLElement;
1891
-
1892
- // 点击的x坐标
1893
- const clickPosX = clickEvent?.clientX != null ? clickEvent.clientX : touchEvent.touches[0].clientX;
1894
- // 点击的y坐标
1895
- const clickPosY = clickEvent?.clientY != null ? clickEvent.clientY : touchEvent.touches[0].clientY;
1896
- const {
1897
- /* 要检测的元素的相对屏幕的横坐标最左边 */
1898
- left: elementPosXLeft,
1899
- /* 要检测的元素的相对屏幕的横坐标最右边 */
1900
- right: elementPosXRight,
1901
- /* 要检测的元素的相对屏幕的纵坐标最上边 */
1902
- top: elementPosYTop,
1903
- /* 要检测的元素的相对屏幕的纵坐标最下边 */
1904
- bottom: elementPosYBottom,
1905
- } = (<HTMLElement>$el).getBoundingClientRect();
1906
- if (
1907
- clickPosX >= elementPosXLeft &&
1908
- clickPosX <= elementPosXRight &&
1909
- clickPosY >= elementPosYTop &&
1910
- clickPosY <= elementPosYBottom
1911
- ) {
1912
- return true;
1913
- } else if (($click && $el.contains($click)) || $click == $el) {
1914
- /* 这种情况是应对在界面中隐藏的元素,getBoundingClientRect获取的都是0 */
1915
- return true;
1916
- } else {
1917
- return false;
1918
- }
1919
- }
1920
- /**
1921
- * 删除某个父元素,父元素可能在上层或上上层或上上上层...
1922
- * @param $el 当前元素
1923
- * @param parentSelector 判断是否满足父元素,参数为当前处理的父元素,满足返回true,否则false
1924
- * @returns
1925
- * + true 已删除
1926
- * + false 未删除
1927
- * @example
1928
- * DOMUtils.deleteParentNode(document.querySelector("a"),".xxx");
1929
- * > true
1930
- **/
1931
- deleteParentNode($el: Node | HTMLElement | Element | null, parentSelector: string): boolean;
1932
- deleteParentNode($el: Node | HTMLElement | Element | null, parentSelector: string) {
1933
- if ($el == null) {
1934
- return;
1935
- }
1936
- if (!CommonUtils.isDOM($el)) {
1937
- throw new Error("DOMUtils.deleteParentNode 参数 target 必须为 Node|HTMLElement 类型");
1938
- }
1939
- if (typeof parentSelector !== "string") {
1940
- throw new Error("DOMUtils.deleteParentNode 参数 targetSelector 必须为 string 类型");
1941
- }
1942
- let result = false;
1943
- const $parent = domUtils.closest($el as HTMLElement, parentSelector);
1944
- if ($parent) {
1945
- this.remove($parent);
1946
- result = true;
1947
- }
1948
- return result;
1949
- }
1950
- /**
1951
- * 定位元素上的字符串,返回一个迭代器
1952
- * @param $el 目标元素
1953
- * @param text 需要定位的字符串
1954
- * @param filter (可选)过滤器函数,返回值为true是排除该元素
1955
- * @example
1956
- * let textIterator = DOMUtils.findElementsWithText(document.documentElement,"xxxx");
1957
- * textIterator.next();
1958
- * > {value: ?HTMLElement, done: boolean, next: Function}
1959
- */
1960
- findElementsWithText<T extends HTMLElement | Element | Node>(
1961
- $el: T,
1962
- text: string,
1963
- filter?: (element: T) => boolean
1964
- ): Generator<HTMLElement | ChildNode, void, any>;
1965
- *findElementsWithText<T extends HTMLElement | Element | Node>(
1966
- $el: T,
1967
- text: string,
1968
- filter?: (element: T) => boolean
1969
- ) {
1970
- const that = this;
1971
- if ((<HTMLElement>$el).outerHTML.includes(text)) {
1972
- if ((<HTMLElement>$el).children.length === 0) {
1973
- const filterResult = typeof filter === "function" ? filter($el) : false;
1974
- if (!filterResult) {
1975
- yield $el as any;
1976
- }
1977
- } else {
1978
- const $text = Array.from($el.childNodes).filter(($child) => $child.nodeType === Node.TEXT_NODE);
1979
- for (const $child of $text) {
1980
- if ((<HTMLElement>$child).textContent.includes(text)) {
1981
- const filterResult = typeof filter === "function" ? filter($el) : false;
1982
- if (!filterResult) {
1983
- yield $child;
1984
- }
1985
- }
1986
- }
1987
- }
1988
- }
1989
-
1990
- for (let index = 0; index < (<HTMLElement>$el).children.length; index++) {
1991
- const $child = (<HTMLElement>$el).children[index] as T;
1992
- yield* that.findElementsWithText($child, text, filter);
1993
- }
1994
- }
1995
- /**
1996
- * 寻找可见元素,如果元素不可见,则向上找它的父元素直至找到,如果父元素不存在则返回null
1997
- * @param $el
1998
- * @example
1999
- * let visibleElement = DOMUtils.findVisibleElement(document.querySelector("a.xx"));
2000
- * > <HTMLElement>
2001
- */
2002
- findVisibleElement($el: HTMLElement | Element | Node) {
2003
- let $current = $el as HTMLElement;
2004
- while ($current) {
2005
- const rect = $current.getBoundingClientRect();
2006
- if ((rect as any).length) {
2007
- return $current;
2008
- }
2009
- $current = $current.parentElement as HTMLElement;
2010
- }
2011
- return null;
2012
- }
2013
- /**
2014
- * 将元素上的文本或元素使用光标进行选中
2015
- *
2016
- * 注意,如果设置startIndex和endIndex,且元素上并无可选则的坐标,那么会报错
2017
- * @param $el 目标元素
2018
- * @param childTextNode 目标元素下的#text元素
2019
- * @param startIndex (可选)开始坐标,可为空
2020
- * @param endIndex (可选)结束坐标,可为空
2021
- * @example
2022
- * DOMUtils.setElementSelection(document.querySelector("span"));
2023
- */
2024
- setElementSelection(
2025
- $el: HTMLElement | Element | Node,
2026
- childTextNode?: ChildNode,
2027
- startIndex?: number,
2028
- endIndex?: number
2029
- ): void {
2030
- const range = this.windowApi.document.createRange();
2031
- range.selectNodeContents($el);
2032
- if (childTextNode) {
2033
- if (childTextNode.nodeType !== Node.TEXT_NODE) {
2034
- throw new TypeError("childTextNode必须是#text元素");
2035
- }
2036
- if (startIndex != null && endIndex != null) {
2037
- range.setStart(childTextNode, startIndex);
2038
- range.setEnd(childTextNode, endIndex);
2039
- }
2040
- }
2041
-
2042
- const selection = this.windowApi.globalThis.getSelection();
2043
- if (selection) {
2044
- selection.removeAllRanges();
2045
- selection.addRange(range);
2046
- }
2047
- }
2048
- }
2049
-
2050
- const domUtils = new DOMUtils();
2051
-
2052
- export { domUtils as DOMUtils };
1
+ import { version } from "../package.json";
2
+ import { CommonUtils } from "./CommonUtils";
3
+ import { ElementHandler } from "./ElementHandler";
4
+ import { type DOMUtilsCSSProperty, type DOMUtilsCSSPropertyType } from "./types/DOMUtilsCSSProperty";
5
+ import type { DOMUtilsCreateElementAttributesMap } from "./types/DOMUtilsEvent";
6
+ import type { DOMUtilsTargetElementType } from "./types/global";
7
+ import type { WindowApiOption } from "./types/WindowApi";
8
+
9
+ class DOMUtils extends ElementHandler {
10
+ constructor(option?: WindowApiOption) {
11
+ super(option);
12
+ }
13
+ /** 版本号 */
14
+ version = version;
15
+ /**
16
+ * 取消挂载在window下的DOMUtils并返回DOMUtils
17
+ * @example
18
+ * let DOMUtils = window.DOMUtils.noConflict()
19
+ */
20
+ noConflict() {
21
+ const that = this;
22
+ if (that.windowApi.window.DOMUtils) {
23
+ CommonUtils.delete(window, "DOMUtils");
24
+ }
25
+ that.windowApi.window.DOMUtils = this;
26
+ return this;
27
+ }
28
+ /**
29
+ * 获取元素的属性值
30
+ * @param $el 目标元素
31
+ * @param attrName 属性名
32
+ * @example
33
+ * // 获取a.xx元素的href属性
34
+ * DOMUtils.attr(document.querySelector("a.xx"),"href");
35
+ * DOMUtils.attr("a.xx","href");
36
+ * > https://xxxx....
37
+ */
38
+ attr($el: DOMUtilsTargetElementType | Element, attrName: string): string;
39
+ /**
40
+ * 设置元素的属性值
41
+ * @param $el 目标元素
42
+ * @param attrName 属性名
43
+ * @param attrValue 属性值
44
+ * @example
45
+ * // 修改a.xx元素的href属性为abcd
46
+ * DOMUtils.attr(document.querySelector("a.xx"),"href","abcd");
47
+ * DOMUtils.attr("a.xx","href","abcd");
48
+ */
49
+ attr($el: DOMUtilsTargetElementType | Element, attrName: string, attrValue: string | boolean | number): void;
50
+ attr($el: DOMUtilsTargetElementType | Element, attrName: string, attrValue?: any) {
51
+ const that = this;
52
+ if (typeof $el === "string") {
53
+ $el = that.selectorAll($el);
54
+ }
55
+ if ($el == null) {
56
+ return;
57
+ }
58
+ if (CommonUtils.isNodeList($el)) {
59
+ if (attrValue == null) {
60
+ // 获取属性
61
+ return that.attr($el[0] as HTMLElement, attrName, attrValue);
62
+ } else {
63
+ // 设置属性
64
+ $el.forEach(($elItem) => {
65
+ that.attr($elItem as HTMLElement, attrName, attrValue);
66
+ });
67
+ return;
68
+ }
69
+ }
70
+ if (attrValue == null) {
71
+ return $el.getAttribute(attrName);
72
+ } else {
73
+ $el.setAttribute(attrName, attrValue);
74
+ }
75
+ }
76
+ /**
77
+ * 创建元素
78
+ * @param tagName 标签名
79
+ * @param property 属性
80
+ * @param attributes 元素上的自定义属性
81
+ * @example
82
+ * // 创建一个DIV元素,且属性class为xxx
83
+ * DOMUtils.createElement("div",undefined,{ class:"xxx" });
84
+ * > <div class="xxx"></div>
85
+ * @example
86
+ * // 创建一个DIV元素
87
+ * DOMUtils.createElement("div");
88
+ * > <div></div>
89
+ * @example
90
+ * // 创建一个DIV元素
91
+ * DOMUtils.createElement("div","测试");
92
+ * > <div>测试</div>
93
+ */
94
+ createElement<K extends keyof HTMLElementTagNameMap>(
95
+ /** 元素名 */
96
+ tagName: K,
97
+ /** 属性 */
98
+ property?:
99
+ | ({
100
+ [P in keyof HTMLElementTagNameMap[K]]?: HTMLElementTagNameMap[K][P];
101
+ } & {
102
+ [key: string]: any;
103
+ })
104
+ | string,
105
+ /** 自定义属性 */
106
+ attributes?: DOMUtilsCreateElementAttributesMap
107
+ ): HTMLElementTagNameMap[K];
108
+ /**
109
+ * 创建元素
110
+ * @param tagName 自定义的标签名
111
+ * @param property 属性
112
+ * @param attributes 元素上的自定义属性
113
+ * @example
114
+ * // 创建一个custom-div元素,且属性class为xxx
115
+ * DOMUtils.createElement("custom-div",undefined,{ class:"xxx" });
116
+ * > <custom-div class="xxx"></custom-div>
117
+ * @example
118
+ * // 创建一个custom-div元素
119
+ * DOMUtils.createElement("custom-div");
120
+ * > <custom-div></custom-div>
121
+ * @example
122
+ * // 创建一个custom-div元素
123
+ * DOMUtils.createElement("custom-div","测试");
124
+ * > <custom-div>测试</custom-div>
125
+ */
126
+ createElement(
127
+ /** 元素名 */
128
+ tagName: string,
129
+ /** 属性 */
130
+ property?:
131
+ | ({
132
+ [P in keyof HTMLElement]?: HTMLElement[P];
133
+ } & {
134
+ [key: string]: any;
135
+ })
136
+ | string,
137
+ /** 自定义属性 */
138
+ attributes?: DOMUtilsCreateElementAttributesMap
139
+ ): HTMLElement;
140
+ createElement<K extends keyof HTMLElementTagNameMap>(
141
+ /** 元素名 */
142
+ tagName: K,
143
+ /** 属性 */
144
+ property?:
145
+ | ({
146
+ [P in keyof HTMLElementTagNameMap[K]]?: HTMLElementTagNameMap[K][P];
147
+ } & {
148
+ [key: string]: any;
149
+ })
150
+ | string,
151
+ /** 自定义属性 */
152
+ attributes?: DOMUtilsCreateElementAttributesMap
153
+ ): HTMLElementTagNameMap[K] {
154
+ const that = this;
155
+ const $el = that.windowApi.document.createElement(tagName);
156
+ if (typeof property === "string") {
157
+ that.html($el, property);
158
+ return $el;
159
+ }
160
+ if (property == null) {
161
+ property = {};
162
+ }
163
+ if (attributes == null) {
164
+ attributes = {};
165
+ }
166
+ Object.keys(property).forEach((key) => {
167
+ const value = property[key];
168
+ if (key === "innerHTML") {
169
+ that.html($el, value);
170
+ return;
171
+ }
172
+ (<any>$el)[key] = value;
173
+ });
174
+ Object.keys(attributes).forEach((key) => {
175
+ let value = attributes[key];
176
+ if (typeof value === "object") {
177
+ /* object转字符串 */
178
+ value = JSON.stringify(value);
179
+ } else if (typeof value === "function") {
180
+ /* function转字符串 */
181
+ value = value.toString();
182
+ }
183
+ $el.setAttribute(key, value);
184
+ });
185
+ return $el;
186
+ }
187
+ /**
188
+ * 获取元素的样式属性值
189
+ * @param $el 目标元素
190
+ * @param property 样式属性名或包含多个属性名和属性值的对象
191
+ * @example
192
+ * // 获取元素a.xx的CSS属性display
193
+ * DOMUtils.css(document.querySelector("a.xx"),"display");
194
+ * DOMUtils.css("a.xx","display");
195
+ * > "none"
196
+ * */
197
+ css($el: DOMUtilsTargetElementType, property: DOMUtilsCSSPropertyType): string;
198
+ /**
199
+ * 获取元素的样式属性值
200
+ * @param $el 目标元素
201
+ * @param property 样式属性名或包含多个属性名和属性值的对象
202
+ * @example
203
+ * // 获取元素a.xx的CSS属性display
204
+ * DOMUtils.css(document.querySelector("a.xx"),"display");
205
+ * DOMUtils.css("a.xx","display");
206
+ * > "none"
207
+ * */
208
+ css($el: DOMUtilsTargetElementType, property: string): string;
209
+ /**
210
+ * 设置元素的样式属性
211
+ * @param $el 目标元素
212
+ * @param property 样式属性名或包含多个属性名和属性值的对象
213
+ * @param value 样式属性值
214
+ * @example
215
+ * // 设置元素a.xx的CSS属性display为block
216
+ * DOMUtils.css(document.querySelector("a.xx"),"display","block");
217
+ * DOMUtils.css(document.querySelector("a.xx"),"display","block !important");
218
+ * DOMUtils.css("a.xx","display","block");
219
+ * DOMUtils.css("a.xx","display","block !important");
220
+ * @example
221
+ * // 设置元素a.xx的CSS属性top为10px
222
+ * DOMUtils.css(document.querySelector("a.xx"),"top","10px");
223
+ * DOMUtils.css(document.querySelector("a.xx"),"top",10);
224
+ * */
225
+ css($el: DOMUtilsTargetElementType, property: DOMUtilsCSSPropertyType & string, value: string | number): string;
226
+ /**
227
+ * 设置元素的样式属性
228
+ * @param $el 目标元素
229
+ * @param property 样式属性名或包含多个属性名和属性值的对象
230
+ * @param value 样式属性值
231
+ * @example
232
+ * // 设置元素a.xx的CSS属性display为block
233
+ * DOMUtils.css(document.querySelector("a.xx"),{ display: "block" }});
234
+ * DOMUtils.css(document.querySelector("a.xx"),{ display: "block !important" }});
235
+ * @example
236
+ * // 设置元素a.xx的CSS属性top为10px
237
+ * DOMUtils.css(document.querySelector("a.xx"),{ top: "10px" });
238
+ * DOMUtils.css(document.querySelector("a.xx"),{ top: 10 });
239
+ * */
240
+ css(
241
+ $el: DOMUtilsTargetElementType,
242
+ property:
243
+ | DOMUtilsCSSProperty
244
+ | {
245
+ [key: string]: string | number;
246
+ }
247
+ | string
248
+ ): string;
249
+ css(
250
+ $el: DOMUtilsTargetElementType,
251
+ property: DOMUtilsCSSPropertyType | string | DOMUtilsCSSProperty,
252
+ value?: string | number
253
+ ) {
254
+ const that = this;
255
+ /**
256
+ * 把纯数字没有px的加上
257
+ */
258
+ function handlePixe(propertyName: string, propertyValue: string | number) {
259
+ const allowAddPixe = ["width", "height", "top", "left", "right", "bottom", "font-size"];
260
+ if (typeof propertyValue === "number") {
261
+ propertyValue = propertyValue.toString();
262
+ }
263
+ if (typeof propertyValue === "string" && allowAddPixe.includes(propertyName) && propertyValue.match(/[0-9]$/gi)) {
264
+ propertyValue = propertyValue + "px";
265
+ }
266
+ return propertyValue;
267
+ }
268
+ if (typeof $el === "string") {
269
+ $el = that.selectorAll($el);
270
+ }
271
+ if ($el == null) {
272
+ return;
273
+ }
274
+ if (CommonUtils.isNodeList($el)) {
275
+ if (typeof property === "string") {
276
+ if (value == null) {
277
+ // 获取属性
278
+ return that.css($el[0] as HTMLElement, property);
279
+ } else {
280
+ // 设置属性
281
+ $el.forEach(($elItem) => {
282
+ that.css($elItem as HTMLElement, property);
283
+ });
284
+ return;
285
+ }
286
+ } else if (typeof property === "object") {
287
+ // 设置属性
288
+ $el.forEach(($elItem) => {
289
+ that.css($elItem as HTMLElement, property as DOMUtilsCSSProperty);
290
+ });
291
+ return;
292
+ }
293
+ return;
294
+ }
295
+ const setStyleProperty = (propertyName: string, propertyValue: string | number) => {
296
+ if (typeof propertyValue === "string" && propertyValue.trim().endsWith("!important")) {
297
+ propertyValue = propertyValue
298
+ .trim()
299
+ .replace(/!important$/gi, "")
300
+ .trim();
301
+ $el.style.setProperty(propertyName, propertyValue, "important");
302
+ } else {
303
+ propertyValue = handlePixe(propertyName, propertyValue);
304
+ $el.style.setProperty(propertyName, propertyValue);
305
+ }
306
+ };
307
+ if (typeof property === "string") {
308
+ if (value == null) {
309
+ return that.windowApi.globalThis.getComputedStyle($el).getPropertyValue(property);
310
+ } else {
311
+ setStyleProperty(property, value);
312
+ }
313
+ } else if (typeof property === "object") {
314
+ for (const prop in property) {
315
+ const value = property[prop as keyof typeof property];
316
+ setStyleProperty(prop, value!);
317
+ }
318
+ } else {
319
+ // 其他情况
320
+ throw new TypeError("property must be string or object");
321
+ }
322
+ }
323
+ /**
324
+ * 获取元素的文本内容,优先返回textContent
325
+ * @param $el 目标元素
326
+ * @returns 如果传入了text,则返回undefined;否则返回文本内容
327
+ * @example
328
+ * // 设置元素a.xx的文本内容为abcd
329
+ * DOMUtils.text(document.querySelector("a.xx"),"abcd")
330
+ * DOMUtils.text("a.xx","abcd")
331
+ * DOMUtils.text("a.xx",document.querySelector("b"))
332
+ * */
333
+ text($el: DOMUtilsTargetElementType | Element | DocumentFragment | Node): string;
334
+ /**
335
+ * 设置元素的文本内容
336
+ * @param $el 目标元素
337
+ * @param text (可选)文本内容
338
+ * @returns 如果传入了text,则返回undefined;否则返回文本内容
339
+ * @example
340
+ * // 设置元素a.xx的文本内容为abcd
341
+ * DOMUtils.text(document.querySelector("a.xx"),"abcd")
342
+ * DOMUtils.text("a.xx","abcd")
343
+ * DOMUtils.text("a.xx",document.querySelector("b"))
344
+ * */
345
+ text(
346
+ $el: DOMUtilsTargetElementType | Element | DocumentFragment | Node,
347
+ text: string | HTMLElement | Element | number
348
+ ): void;
349
+ text($el: DOMUtilsTargetElementType | Element | DocumentFragment | Node, text?: any) {
350
+ const that = this;
351
+ if (typeof $el === "string") {
352
+ $el = that.selectorAll($el);
353
+ }
354
+ if ($el == null) {
355
+ return;
356
+ }
357
+ if (CommonUtils.isNodeList($el)) {
358
+ if (text == null) {
359
+ // 获取
360
+ return that.text($el[0] as HTMLElement);
361
+ } else {
362
+ // 设置
363
+ $el.forEach(($elItem) => {
364
+ that.text($elItem as HTMLElement, text);
365
+ });
366
+ }
367
+ return;
368
+ }
369
+ if (text == null) {
370
+ return $el.textContent || (<HTMLElement>$el).innerText;
371
+ } else {
372
+ if (text instanceof Node) {
373
+ text = text.textContent || (text as HTMLElement).innerText;
374
+ }
375
+ if ("textContent" in $el) {
376
+ $el.textContent = text as string;
377
+ } else if ("innerText" in $el) {
378
+ ($el as HTMLElement).innerText = text as string;
379
+ }
380
+ }
381
+ }
382
+ /**
383
+ * 设置元素的HTML内容
384
+ * @param element 目标元素
385
+ * @param html (可选)HTML内容|元素
386
+ * @returns 如果传入了html,则返回undefined;否则返回HTML内容
387
+ * @example
388
+ * // 设置元素a.xx的文本内容为<b>abcd</b>
389
+ * DOMUtils.html(document.querySelector("a.xx"),"<b>abcd</b>")
390
+ * DOMUtils.html("a.xx","<b>abcd</b>")
391
+ * DOMUtils.html("a.xx",document.querySelector("b"))
392
+ * */
393
+ html(element: DOMUtilsTargetElementType, html: string | HTMLElement | Element | number): void;
394
+ /**
395
+ * 获取元素的HTML内容
396
+ * @param $el 目标元素
397
+ * @param html (可选)HTML内容|元素
398
+ * @returns 如果传入了html,则返回undefined;否则返回HTML内容
399
+ * @example
400
+ * // 设置元素a.xx的文本内容为<b>abcd</b>
401
+ * DOMUtils.html(document.querySelector("a.xx"),"<b>abcd</b>")
402
+ * DOMUtils.html("a.xx","<b>abcd</b>")
403
+ * DOMUtils.html("a.xx",document.querySelector("b"))
404
+ * */
405
+ html($el: DOMUtilsTargetElementType): string;
406
+ html($el: DOMUtilsTargetElementType, html?: any) {
407
+ const that = this;
408
+ if (typeof $el === "string") {
409
+ $el = that.selectorAll($el);
410
+ }
411
+ if ($el == null) {
412
+ return;
413
+ }
414
+ if (CommonUtils.isNodeList($el)) {
415
+ if (html == null) {
416
+ // 获取
417
+ return that.html($el[0] as HTMLElement);
418
+ } else {
419
+ // 设置
420
+ $el.forEach(($elItem) => {
421
+ that.html($elItem as HTMLElement, html);
422
+ });
423
+ }
424
+ return;
425
+ }
426
+ if (html == null) {
427
+ // 获取
428
+ return $el.innerHTML;
429
+ } else {
430
+ // 设置
431
+ if (html instanceof Element) {
432
+ html = html.innerHTML;
433
+ }
434
+ if ("innerHTML" in $el) {
435
+ CommonUtils.setSafeHTML($el, html);
436
+ }
437
+ }
438
+ }
439
+ /**
440
+ * 获取移动元素的transform偏移
441
+ */
442
+ getTransform(
443
+ $el: HTMLElement,
444
+ isShow: boolean = false
445
+ ): {
446
+ transformLeft: number;
447
+ transformTop: number;
448
+ } {
449
+ const that = this;
450
+ let transform_left = 0;
451
+ let transform_top = 0;
452
+ if (!(isShow || (!isShow && CommonUtils.isShow($el)))) {
453
+ /* 未显示 */
454
+ const { recovery } = CommonUtils.forceShow($el);
455
+ const transformInfo = that.getTransform($el, true);
456
+ recovery();
457
+ return transformInfo;
458
+ }
459
+ const elementTransform = that.windowApi.globalThis.getComputedStyle($el).transform;
460
+ if (elementTransform != null && elementTransform !== "none" && elementTransform !== "") {
461
+ const elementTransformSplit = elementTransform.match(/\((.+)\)/)?.[1].split(",");
462
+ if (elementTransformSplit) {
463
+ transform_left = Math.abs(parseInt(elementTransformSplit[4]));
464
+ transform_top = Math.abs(parseInt(elementTransformSplit[5]));
465
+ } else {
466
+ transform_left = 0;
467
+ transform_top = 0;
468
+ }
469
+ }
470
+ return {
471
+ transformLeft: transform_left,
472
+ transformTop: transform_top,
473
+ };
474
+ }
475
+
476
+ /**
477
+ * 设置元素的value属性值
478
+ * @param $el 目标元素
479
+ * @param value (可选)value属性值
480
+ * @returns 如果传入了value,则返回undefined;否则返回value属性值
481
+ * > true
482
+ * @example
483
+ * // 修改元素input.xx的复选框值为true
484
+ * DOMUtils.val(document.querySelector("input.xx"),true)
485
+ * DOMUtils.val("input.xx",true)
486
+ * */
487
+ val(
488
+ $el:
489
+ | HTMLInputElement
490
+ | HTMLTextAreaElement
491
+ | HTMLSelectElement
492
+ | string
493
+ | (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement)[]
494
+ | NodeListOf<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,
495
+ value: string | boolean | number
496
+ ): void;
497
+ /**
498
+ * 获取value属性值
499
+ * @param $el 目标元素
500
+ * @example
501
+ * // 获取元素textarea的值
502
+ * DOMUtils.val(document.querySelector("textarea.xx"))
503
+ * */
504
+ val(
505
+ $el:
506
+ | HTMLInputElement
507
+ | HTMLTextAreaElement
508
+ | HTMLSelectElement
509
+ | string
510
+ | (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement)[]
511
+ | NodeListOf<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
512
+ ): string;
513
+ /**
514
+ * 获取value属性值
515
+ * @param $el 目标元素
516
+ * @example
517
+ * // 获取元素input.xx的复选框值
518
+ * DOMUtils.val(document.querySelector("input.xx"))
519
+ * DOMUtils.val("input.xx")
520
+ * */
521
+ val(
522
+ $el:
523
+ | HTMLInputElement
524
+ | HTMLTextAreaElement
525
+ | HTMLSelectElement
526
+ | (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement)[]
527
+ | NodeListOf<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
528
+ ): boolean | string;
529
+ val(
530
+ $el:
531
+ | HTMLInputElement
532
+ | HTMLTextAreaElement
533
+ | HTMLSelectElement
534
+ | string
535
+ | (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement)[]
536
+ | NodeListOf<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,
537
+ value?: string | boolean | number
538
+ ) {
539
+ const that = this;
540
+ if (typeof $el === "string") {
541
+ $el = that.selectorAll($el);
542
+ }
543
+ if ($el == null) {
544
+ return;
545
+ }
546
+ if (CommonUtils.isNodeList($el)) {
547
+ if (value == null) {
548
+ // 获取
549
+ return that.val($el[0] as HTMLInputElement);
550
+ } else {
551
+ // 设置
552
+ $el.forEach(($elItem) => {
553
+ that.val($elItem as HTMLInputElement, value);
554
+ });
555
+ }
556
+ return;
557
+ }
558
+ if (value == null) {
559
+ // 获取
560
+ if ($el.localName === "input" && ($el.type === "checkbox" || $el.type === "radio")) {
561
+ return ($el as HTMLInputElement).checked;
562
+ } else {
563
+ return $el.value;
564
+ }
565
+ } else {
566
+ // 设置
567
+ if ($el.localName === "input" && ($el.type === "checkbox" || $el.type === "radio")) {
568
+ ($el as HTMLInputElement).checked = !!value;
569
+ } else {
570
+ $el.value = value as string;
571
+ }
572
+ }
573
+ }
574
+ /**
575
+ * 获取元素的属性值
576
+ * @param $el 目标元素
577
+ * @param propName 属性名
578
+ * @param propValue 属性值
579
+ * @example
580
+ * // 获取元素a.xx的属性data-value
581
+ * DOMUtils.val(document.querySelector("a.xx"),"data-value")
582
+ * DOMUtils.val("a.xx","data-value")
583
+ * > undefined
584
+ * */
585
+ prop<T>($el: DOMUtilsTargetElementType | DocumentFragment, propName: string): T;
586
+ /**
587
+ * 设置元素的属性值
588
+ * @param $el 目标元素
589
+ * @param propName 属性名
590
+ * @param propValue 属性值
591
+ * @example
592
+ * // 设置元素a.xx的属性data-value为1
593
+ * DOMUtils.val(document.querySelector("a.xx"),"data-value",1)
594
+ * DOMUtils.val("a.xx","data-value",1)
595
+ * */
596
+ prop<T>($el: DOMUtilsTargetElementType | DocumentFragment, propName: string, propValue: T): void;
597
+ prop($el: DOMUtilsTargetElementType | DocumentFragment, propName: string, propValue?: any) {
598
+ const that = this;
599
+ if (typeof $el === "string") {
600
+ $el = that.selectorAll($el);
601
+ }
602
+ if ($el == null) {
603
+ return;
604
+ }
605
+ if (CommonUtils.isNodeList($el)) {
606
+ if (propValue == null) {
607
+ // 获取
608
+ return that.prop($el[0] as HTMLElement, propName);
609
+ } else {
610
+ // 设置
611
+ $el.forEach(($elItem) => {
612
+ that.prop($elItem as HTMLElement, propName, propValue);
613
+ });
614
+ }
615
+ return;
616
+ }
617
+ if (propValue == null) {
618
+ return Reflect.get($el, propName);
619
+ } else {
620
+ if ($el instanceof Element && propName === "innerHTML") {
621
+ that.html($el, propValue);
622
+ } else {
623
+ Reflect.set($el, propName, propValue);
624
+ }
625
+ }
626
+ }
627
+ /**
628
+ * 移除元素的属性
629
+ * @param $el 目标元素
630
+ * @param attrName 属性名
631
+ * @example
632
+ * // 移除元素a.xx的属性data-value
633
+ * DOMUtils.removeAttr(document.querySelector("a.xx"),"data-value")
634
+ * DOMUtils.removeAttr("a.xx","data-value")
635
+ * */
636
+ removeAttr($el: DOMUtilsTargetElementType | Element, attrName: string) {
637
+ const that = this;
638
+ if (typeof $el === "string") {
639
+ $el = that.selectorAll($el);
640
+ }
641
+ if ($el == null) {
642
+ return;
643
+ }
644
+ if (CommonUtils.isNodeList($el)) {
645
+ // 设置
646
+ $el.forEach(($elItem) => {
647
+ that.removeAttr($elItem as HTMLElement, attrName);
648
+ });
649
+ return;
650
+ }
651
+ $el.removeAttribute(attrName);
652
+ }
653
+ /**
654
+ * 移除元素class名
655
+ * @param $el 目标元素
656
+ * @param className 类名
657
+ * @example
658
+ * // 移除元素a.xx的className为xx
659
+ * DOMUtils.removeClass(document.querySelector("a.xx"),"xx")
660
+ * DOMUtils.removeClass("a.xx","xx")
661
+ */
662
+ removeClass($el: DOMUtilsTargetElementType | Element, className?: string | string[] | undefined | null) {
663
+ const that = this;
664
+ if (typeof $el === "string") {
665
+ $el = that.selectorAll($el);
666
+ }
667
+ if ($el == null) {
668
+ return;
669
+ }
670
+ if (CommonUtils.isNodeList($el)) {
671
+ // 设置
672
+ $el.forEach(($elItem) => {
673
+ that.removeClass($elItem as HTMLElement, className);
674
+ });
675
+ return;
676
+ }
677
+ if (className == null) {
678
+ // 清空全部className
679
+ $el.className = "";
680
+ } else {
681
+ if (!Array.isArray(className)) {
682
+ className = className.trim().split(" ");
683
+ }
684
+ className.forEach((itemClassName) => {
685
+ $el.classList.remove(itemClassName);
686
+ });
687
+ }
688
+ }
689
+ /**
690
+ * 移除元素的属性
691
+ * @param $el 目标元素
692
+ * @param propName 属性名
693
+ * @example
694
+ * // 移除元素a.xx的href属性
695
+ * DOMUtils.removeProp(document.querySelector("a.xx"),"href")
696
+ * DOMUtils.removeProp("a.xx","href")
697
+ * */
698
+ removeProp($el: DOMUtilsTargetElementType | DocumentFragment, propName: string) {
699
+ const that = this;
700
+ if (typeof $el === "string") {
701
+ $el = that.selectorAll($el);
702
+ }
703
+ if ($el == null) {
704
+ return;
705
+ }
706
+ if (CommonUtils.isNodeList($el)) {
707
+ // 设置
708
+ $el.forEach(($elItem) => {
709
+ that.removeProp($elItem as HTMLElement, propName);
710
+ });
711
+ return;
712
+ }
713
+ CommonUtils.delete($el, propName);
714
+ }
715
+ /**
716
+ * 给元素添加class
717
+ * @param $el 目标元素
718
+ * @param className class名
719
+ * @example
720
+ * // 元素a.xx的className添加_vue_
721
+ * DOMUtils.addClass(document.querySelector("a.xx"),"_vue_")
722
+ * DOMUtils.addClass("a.xx","_vue_")
723
+ * */
724
+ addClass($el: DOMUtilsTargetElementType | Element, className: string | string[]) {
725
+ const that = this;
726
+ if (typeof $el === "string") {
727
+ $el = that.selectorAll($el);
728
+ }
729
+ if ($el == null) {
730
+ return;
731
+ }
732
+ if (CommonUtils.isNodeList($el)) {
733
+ // 设置
734
+ $el.forEach(($elItem) => {
735
+ that.addClass($elItem as HTMLElement, className);
736
+ });
737
+ return;
738
+ }
739
+ if (!Array.isArray(className)) {
740
+ className = className.split(" ");
741
+ }
742
+ className.forEach((itemClassName) => {
743
+ if (itemClassName.trim() == "") {
744
+ return;
745
+ }
746
+ $el.classList.add(itemClassName);
747
+ });
748
+ }
749
+ /**
750
+ * 判断元素是否存在className
751
+ * @param $el
752
+ * @param className
753
+ */
754
+ hasClass($el: DOMUtilsTargetElementType | Element, className: string | string[]): boolean {
755
+ const that = this;
756
+ if (typeof $el === "string") {
757
+ $el = that.selectorAll($el);
758
+ }
759
+ if ($el == null) {
760
+ return false;
761
+ }
762
+ if (CommonUtils.isNodeList($el)) {
763
+ let flag = true;
764
+ for (let index = 0; index < $el.length; index++) {
765
+ const $elItem = $el[index] as HTMLElement;
766
+ flag = flag && that.hasClass($elItem, className);
767
+ }
768
+ return flag;
769
+ }
770
+ if (!$el?.classList) {
771
+ return false;
772
+ }
773
+ if (!Array.isArray(className)) {
774
+ className = className.split(" ");
775
+ }
776
+ for (let index = 0; index < className.length; index++) {
777
+ const item = className[index].trim();
778
+ if (!$el.classList.contains(item)) {
779
+ return false;
780
+ }
781
+ }
782
+ return true;
783
+ }
784
+ /**
785
+ * 函数在元素内部末尾添加子元素或HTML字符串
786
+ * @param $el 目标元素
787
+ * @param args 子元素或HTML字符串
788
+ * @example
789
+ * // 元素a.xx的内部末尾添加一个元素
790
+ * DOMUtils.append(document.querySelector("a.xx"), document.querySelector("b.xx"))
791
+ * DOMUtils.append("a.xx", "<b class="xx"></b>")
792
+ * DOMUtils.append(document, [document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx")])
793
+ * DOMUtils.append(document, document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx"))
794
+ * */
795
+ append(
796
+ $el: DOMUtilsTargetElementType | DocumentFragment,
797
+ ...args: (
798
+ | HTMLElement
799
+ | string
800
+ | Element
801
+ | DocumentFragment
802
+ | (HTMLElement | string | Element | DocumentFragment)[]
803
+ | NodeList
804
+ )[]
805
+ ) {
806
+ if (typeof $el === "string") {
807
+ $el = this.selectorAll($el);
808
+ }
809
+
810
+ if ($el == null) {
811
+ return;
812
+ }
813
+
814
+ if (CommonUtils.isNodeList($el)) {
815
+ // 设置
816
+ $el.forEach(($elItem) => {
817
+ this.append($elItem as HTMLElement, ...args);
818
+ });
819
+ return;
820
+ }
821
+ const handler = ($ele: HTMLElement | DocumentFragment, $target: HTMLElement | string | Node) => {
822
+ if ($ele instanceof DocumentFragment) {
823
+ if (typeof $target === "string") {
824
+ // 字符串转元素
825
+ $target = this.toElement($target, true, false);
826
+ }
827
+ $ele.appendChild($target);
828
+ } else {
829
+ if (typeof $target === "string") {
830
+ $ele.insertAdjacentHTML("beforeend", CommonUtils.createSafeHTML($target));
831
+ } else {
832
+ $ele.appendChild($target);
833
+ }
834
+ }
835
+ };
836
+ const $fragment = this.windowApi.document.createDocumentFragment();
837
+ args.forEach((argItem) => {
838
+ if (CommonUtils.isNodeList(argItem)) {
839
+ // 数组
840
+ argItem.forEach((it) => {
841
+ handler($fragment, it);
842
+ });
843
+ } else {
844
+ handler($fragment, argItem);
845
+ }
846
+ });
847
+ handler($el, $fragment);
848
+ }
849
+ /**
850
+ * 函数 在元素内部开头添加子元素或HTML字符串
851
+ * @param $el 目标元素
852
+ * @param args 子元素或HTML字符串
853
+ * @example
854
+ * // 元素a.xx内部开头添加一个元素
855
+ * DOMUtils.prepend(document.querySelector("a.xx"),document.querySelector("b.xx"))
856
+ * DOMUtils.prepend("a.xx","'<b class="xx"></b>")
857
+ * DOMUtils.prepend(document, [document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx")])
858
+ * DOMUtils.prepend(document, document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx"))
859
+ * */
860
+ prepend(
861
+ $el: DOMUtilsTargetElementType | DocumentFragment,
862
+ ...args: (
863
+ | HTMLElement
864
+ | string
865
+ | Element
866
+ | DocumentFragment
867
+ | (HTMLElement | string | Element | DocumentFragment)[]
868
+ | NodeList
869
+ )[]
870
+ ) {
871
+ if (typeof $el === "string") {
872
+ $el = this.selectorAll($el);
873
+ }
874
+
875
+ if ($el == null) {
876
+ return;
877
+ }
878
+
879
+ if (CommonUtils.isNodeList($el)) {
880
+ // 设置
881
+ $el.forEach(($elItem) => {
882
+ this.prepend($elItem as HTMLElement, ...args);
883
+ });
884
+ return;
885
+ }
886
+ const handler = ($ele: HTMLElement | DocumentFragment, $target: HTMLElement | string | Node) => {
887
+ if ($ele instanceof DocumentFragment) {
888
+ if (typeof $target === "string") {
889
+ // 字符串转元素
890
+ $target = this.toElement($target, true, false);
891
+ }
892
+ $ele.appendChild($target);
893
+ } else {
894
+ if (typeof $target === "string") {
895
+ $ele.insertAdjacentHTML("afterbegin", CommonUtils.createSafeHTML($target));
896
+ } else {
897
+ const $firstChild = $ele.firstChild;
898
+ if ($firstChild) {
899
+ $ele.insertBefore($target, $firstChild);
900
+ } else {
901
+ $ele.prepend($target);
902
+ }
903
+ }
904
+ }
905
+ };
906
+ const $fragment = this.windowApi.document.createDocumentFragment();
907
+ args.forEach((argItem) => {
908
+ if (CommonUtils.isNodeList(argItem)) {
909
+ // 数组
910
+ argItem.forEach((it) => {
911
+ handler($fragment, it);
912
+ });
913
+ } else {
914
+ handler($fragment, argItem);
915
+ }
916
+ });
917
+ handler($el, $fragment);
918
+ }
919
+ /**
920
+ * 在元素后面添加兄弟元素或HTML字符串
921
+ * @param $el 目标元素
922
+ * @param args 兄弟元素或HTML字符串
923
+ * @example
924
+ * // 元素a.xx后面添加一个元素
925
+ * DOMUtils.after(document.querySelector("a.xx"),document.querySelector("b.xx"))
926
+ * DOMUtils.after("a.xx","'<b class="xx"></b>")
927
+ * DOMUtils.after(document, [document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx")])
928
+ * DOMUtils.after(document, document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx"))
929
+ * */
930
+ after(
931
+ $el: DOMUtilsTargetElementType,
932
+ ...args: (
933
+ | HTMLElement
934
+ | string
935
+ | Element
936
+ | DocumentFragment
937
+ | (HTMLElement | string | Element | DocumentFragment)[]
938
+ | NodeList
939
+ )[]
940
+ ) {
941
+ if (typeof $el === "string") {
942
+ $el = this.selectorAll($el);
943
+ }
944
+
945
+ if ($el == null) {
946
+ return;
947
+ }
948
+
949
+ if (CommonUtils.isNodeList($el)) {
950
+ // 设置
951
+ $el.forEach(($elItem) => {
952
+ this.after($elItem as HTMLElement, ...args);
953
+ });
954
+ return;
955
+ }
956
+ const handler = ($ele: HTMLElement | DocumentFragment, $target: HTMLElement | string | Node) => {
957
+ if ($ele instanceof DocumentFragment) {
958
+ if (typeof $target === "string") {
959
+ // 字符串转元素
960
+ $target = this.toElement($target, true, false);
961
+ }
962
+ $ele.appendChild($target);
963
+ } else {
964
+ if (typeof $target === "string") {
965
+ $ele.insertAdjacentHTML("afterend", CommonUtils.createSafeHTML($target));
966
+ } else {
967
+ const $parent = $el.parentElement;
968
+ const $nextSlibling = $el.nextSibling;
969
+ if ($parent && $nextSlibling) {
970
+ $parent.insertBefore($target, $nextSlibling);
971
+ } else {
972
+ $el.after($target);
973
+ }
974
+ }
975
+ }
976
+ };
977
+ const $fragment = this.windowApi.document.createDocumentFragment();
978
+ args.forEach((argItem) => {
979
+ if (CommonUtils.isNodeList(argItem)) {
980
+ // 数组
981
+ argItem.forEach((it) => {
982
+ handler($fragment, it);
983
+ });
984
+ } else {
985
+ handler($fragment, argItem);
986
+ }
987
+ });
988
+ handler($el, $fragment);
989
+ }
990
+ /**
991
+ * 在元素前面添加兄弟元素或HTML字符串
992
+ * @param $el 目标元素
993
+ * @param args 兄弟元素或HTML字符串
994
+ * @example
995
+ * // 元素a.xx前面添加一个元素
996
+ * DOMUtils.before(document.querySelector("a.xx"),document.querySelector("b.xx"))
997
+ * DOMUtils.before("a.xx","'<b class="xx"></b>")
998
+ * DOMUtils.before(document, [document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx")])
999
+ * DOMUtils.before(document, document.querySelector("b.xx"), document.querySelector("c.xx"), document.querySelector("d.xx"))
1000
+ *
1001
+ * */
1002
+ before(
1003
+ $el: DOMUtilsTargetElementType,
1004
+ ...args: (
1005
+ | HTMLElement
1006
+ | string
1007
+ | Element
1008
+ | DocumentFragment
1009
+ | (HTMLElement | string | Element | DocumentFragment)[]
1010
+ | NodeList
1011
+ )[]
1012
+ ) {
1013
+ if (typeof $el === "string") {
1014
+ $el = this.selectorAll($el);
1015
+ }
1016
+
1017
+ if ($el == null) {
1018
+ return;
1019
+ }
1020
+
1021
+ if (CommonUtils.isNodeList($el)) {
1022
+ // 设置
1023
+ $el.forEach(($elItem) => {
1024
+ this.before($elItem as HTMLElement, ...args);
1025
+ });
1026
+ return;
1027
+ }
1028
+ const handler = ($ele: HTMLElement | DocumentFragment, $target: HTMLElement | string | Node) => {
1029
+ if ($ele instanceof DocumentFragment) {
1030
+ if (typeof $target === "string") {
1031
+ // 字符串转元素
1032
+ $target = this.toElement($target, true, false);
1033
+ }
1034
+ $ele.appendChild($target);
1035
+ } else {
1036
+ if (typeof $target === "string") {
1037
+ $el.insertAdjacentHTML("beforebegin", CommonUtils.createSafeHTML($target));
1038
+ } else {
1039
+ const $parent = $el.parentElement;
1040
+ if ($parent) {
1041
+ $parent.insertBefore($target, $el);
1042
+ } else {
1043
+ $el.before($target);
1044
+ }
1045
+ }
1046
+ }
1047
+ };
1048
+ const $fragment = this.windowApi.document.createDocumentFragment();
1049
+ args.forEach((argItem) => {
1050
+ if (CommonUtils.isNodeList(argItem)) {
1051
+ // 数组
1052
+ argItem.forEach((it) => {
1053
+ handler($fragment, it);
1054
+ });
1055
+ } else {
1056
+ handler($fragment, argItem);
1057
+ }
1058
+ });
1059
+ handler($el, $fragment);
1060
+ }
1061
+ /**
1062
+ * 移除元素
1063
+ * @param $el 目标元素,可以是数组、单个元素、NodeList、元素选择器
1064
+ * @example
1065
+ * DOMUtils.remove(document.querySelector("a.xx"))
1066
+ * DOMUtils.remove(document.querySelectorAll("a.xx"))
1067
+ * DOMUtils.remove("a.xx")
1068
+ * DOMUtils.remove([a.xxx, div.xxx, span.xxx])
1069
+ * */
1070
+ remove($el: DOMUtilsTargetElementType | Element) {
1071
+ const that = this;
1072
+ if (typeof $el === "string") {
1073
+ $el = that.selectorAll($el);
1074
+ }
1075
+ if ($el == null) {
1076
+ return;
1077
+ }
1078
+ if (CommonUtils.isNodeList($el)) {
1079
+ $el.forEach(($elItem) => {
1080
+ that.remove($elItem as HTMLElement);
1081
+ });
1082
+ return;
1083
+ }
1084
+ if (typeof $el.remove === "function") {
1085
+ $el.remove();
1086
+ } else if ($el.parentElement) {
1087
+ $el.parentElement.removeChild($el);
1088
+ } else if ($el.parentNode) {
1089
+ $el.parentNode.removeChild($el);
1090
+ }
1091
+ }
1092
+ /**
1093
+ * 移除元素内所有的子元素
1094
+ * @param $el 目标元素
1095
+ * @example
1096
+ * // 移除元素a.xx元素的所有子元素
1097
+ * DOMUtils.empty(document.querySelector("a.xx"))
1098
+ * DOMUtils.empty("a.xx")
1099
+ * */
1100
+ empty($el: DOMUtilsTargetElementType | Element) {
1101
+ const that = this;
1102
+ if (typeof $el === "string") {
1103
+ $el = that.selectorAll($el);
1104
+ }
1105
+ if ($el == null) {
1106
+ return;
1107
+ }
1108
+ if (CommonUtils.isNodeList($el)) {
1109
+ // 设置
1110
+ $el.forEach(($elItem) => {
1111
+ that.empty($elItem as HTMLElement);
1112
+ });
1113
+ return;
1114
+ }
1115
+ if ($el.innerHTML) {
1116
+ $el.innerHTML = "";
1117
+ } else if ($el.textContent) {
1118
+ $el.textContent = "";
1119
+ }
1120
+ }
1121
+ /**
1122
+ * 获取元素相对于文档的偏移坐标(加上文档的滚动条)
1123
+ * @param $el 目标元素
1124
+ * @example
1125
+ * // 获取元素a.xx的对于文档的偏移坐标
1126
+ * DOMUtils.offset(document.querySelector("a.xx"))
1127
+ * DOMUtils.offset("a.xx")
1128
+ * > 0
1129
+ */
1130
+ offset($el: HTMLElement | string) {
1131
+ const that = this;
1132
+ if (typeof $el === "string") {
1133
+ $el = that.selector($el) as HTMLElement;
1134
+ }
1135
+ if ($el == null) {
1136
+ return;
1137
+ }
1138
+
1139
+ const rect = $el.getBoundingClientRect();
1140
+ return {
1141
+ /** y轴偏移 */
1142
+ top: rect.top + that.windowApi.globalThis.scrollY,
1143
+ /** x轴偏移 */
1144
+ left: rect.left + that.windowApi.globalThis.scrollX,
1145
+ };
1146
+ }
1147
+ /**
1148
+ * 获取元素的宽度
1149
+ * @param $el 要获取宽度的元素
1150
+ * @param value 宽度值
1151
+ * @param isShow 是否已进行isShow,避免爆堆栈
1152
+ * @returns 元素的宽度,单位为像素
1153
+ * @example
1154
+ * // 获取元素a.xx的宽度
1155
+ * DOMUtils.width(document.querySelector("a.xx"))
1156
+ * DOMUtils.width("a.xx")
1157
+ * > 100
1158
+ * // 获取window的宽度
1159
+ * DOMUtils.width(window)
1160
+ * > 400
1161
+ * @example
1162
+ * // 设置元素a.xx的宽度为200
1163
+ * DOMUtils.width(document.querySelector("a.xx"),200)
1164
+ * DOMUtils.width("a.xx",200)
1165
+ */
1166
+ width($el: HTMLElement | string | Window | typeof globalThis | Document, isShow?: boolean): number;
1167
+ width($el: HTMLElement | string | Window | typeof globalThis | Document, isShow: boolean = false): number {
1168
+ const that = this;
1169
+ if (typeof $el === "string") {
1170
+ $el = that.selector<HTMLElement>($el)!;
1171
+ }
1172
+ if (CommonUtils.isWin($el)) {
1173
+ return that.windowApi.window.document.documentElement.clientWidth;
1174
+ }
1175
+ if (($el as HTMLElement).nodeType === 9) {
1176
+ /* Document文档节点 */
1177
+ $el = $el as Document;
1178
+ return Math.max(
1179
+ $el.body.scrollWidth,
1180
+ $el.documentElement.scrollWidth,
1181
+ $el.body.offsetWidth,
1182
+ $el.documentElement.offsetWidth,
1183
+ $el.documentElement.clientWidth
1184
+ );
1185
+ }
1186
+ if (isShow || (!isShow && CommonUtils.isShow($el as HTMLElement))) {
1187
+ /* 已显示 */
1188
+ /* 不从style中获取对应的宽度,因为可能使用了class定义了width !important */
1189
+ $el = $el as HTMLElement;
1190
+ /* 如果element.style.width为空 则从css里面获取是否定义了width信息如果定义了 则读取css里面定义的宽度width */
1191
+ if (parseFloat(CommonUtils.getStyleValue($el, "width").toString()) > 0) {
1192
+ return parseFloat(CommonUtils.getStyleValue($el, "width").toString());
1193
+ }
1194
+
1195
+ /* 如果从css里获取到的值不是大于0 可能是auto 则通过offsetWidth来进行计算 */
1196
+ if ($el.offsetWidth > 0) {
1197
+ const borderLeftWidth = CommonUtils.getStyleValue($el, "borderLeftWidth");
1198
+ const borderRightWidth = CommonUtils.getStyleValue($el, "borderRightWidth");
1199
+ const paddingLeft = CommonUtils.getStyleValue($el, "paddingLeft");
1200
+ const paddingRight = CommonUtils.getStyleValue($el, "paddingRight");
1201
+ const backHeight =
1202
+ parseFloat($el.offsetWidth.toString()) -
1203
+ parseFloat(borderLeftWidth.toString()) -
1204
+ parseFloat(borderRightWidth.toString()) -
1205
+ parseFloat(paddingLeft.toString()) -
1206
+ parseFloat(paddingRight.toString());
1207
+ return parseFloat(backHeight.toString());
1208
+ }
1209
+ return 0;
1210
+ } else {
1211
+ /* 未显示 */
1212
+ $el = $el as HTMLElement;
1213
+ const { recovery } = CommonUtils.forceShow($el);
1214
+ const width = that.width($el, true);
1215
+ recovery();
1216
+ return width;
1217
+ }
1218
+ }
1219
+
1220
+ /**
1221
+ * 获取元素的高度
1222
+ * @param $el 要获取高度的元素
1223
+ * @param isShow 是否已进行isShow,避免爆堆栈
1224
+ * @returns 元素的高度,单位为像素
1225
+ * @example
1226
+ * // 获取元素a.xx的高度
1227
+ * DOMUtils.height(document.querySelector("a.xx"))
1228
+ * DOMUtils.height("a.xx")
1229
+ * > 100
1230
+ * // 获取window的高度
1231
+ * DOMUtils.height(window)
1232
+ * > 700
1233
+ * @example
1234
+ * // 设置元素a.xx的高度为200
1235
+ * DOMUtils.height(document.querySelector("a.xx"),200)
1236
+ * DOMUtils.height("a.xx",200)
1237
+ */
1238
+ height($el: HTMLElement | string | Window | typeof globalThis | Document, isShow?: boolean): number;
1239
+ height($el: HTMLElement | string | Window | typeof globalThis | Document, isShow: boolean = false): number {
1240
+ const that = this;
1241
+ if (CommonUtils.isWin($el)) {
1242
+ return that.windowApi.window.document.documentElement.clientHeight;
1243
+ }
1244
+ if (typeof $el === "string") {
1245
+ $el = that.selector($el) as HTMLElement;
1246
+ }
1247
+ if (($el as Document).nodeType === 9) {
1248
+ $el = $el as Document;
1249
+ /* Document文档节点 */
1250
+ return Math.max(
1251
+ $el.body.scrollHeight,
1252
+ $el.documentElement.scrollHeight,
1253
+ $el.body.offsetHeight,
1254
+ $el.documentElement.offsetHeight,
1255
+ $el.documentElement.clientHeight
1256
+ );
1257
+ }
1258
+ if (isShow || (!isShow && CommonUtils.isShow($el as HTMLElement))) {
1259
+ $el = $el as HTMLElement;
1260
+ /* 已显示 */
1261
+ /* 从style中获取对应的高度,因为可能使用了class定义了width !important */
1262
+ /* 如果element.style.height为空 则从css里面获取是否定义了height信息如果定义了 则读取css里面定义的高度height */
1263
+ if (parseFloat(CommonUtils.getStyleValue($el, "height").toString()) > 0) {
1264
+ return parseFloat(CommonUtils.getStyleValue($el, "height").toString());
1265
+ }
1266
+
1267
+ /* 如果从css里获取到的值不是大于0 可能是auto 则通过offsetHeight来进行计算 */
1268
+ if ($el.offsetHeight > 0) {
1269
+ const borderTopWidth = CommonUtils.getStyleValue($el, "borderTopWidth");
1270
+ const borderBottomWidth = CommonUtils.getStyleValue($el, "borderBottomWidth");
1271
+ const paddingTop = CommonUtils.getStyleValue($el, "paddingTop");
1272
+ const paddingBottom = CommonUtils.getStyleValue($el, "paddingBottom");
1273
+ const backHeight =
1274
+ parseFloat($el.offsetHeight.toString()) -
1275
+ parseFloat(borderTopWidth.toString()) -
1276
+ parseFloat(borderBottomWidth.toString()) -
1277
+ parseFloat(paddingTop.toString()) -
1278
+ parseFloat(paddingBottom.toString());
1279
+ return parseFloat(backHeight.toString());
1280
+ }
1281
+ return 0;
1282
+ } else {
1283
+ /* 未显示 */
1284
+ $el = $el as HTMLElement;
1285
+ const { recovery } = CommonUtils.forceShow($el);
1286
+ const height = that.height($el, true);
1287
+ recovery();
1288
+ return height;
1289
+ }
1290
+ }
1291
+ /**
1292
+ * 获取元素的外部宽度(包括边框和外边距)
1293
+ * @param $el 要获取外部宽度的元素
1294
+ * @param [isShow=false] 是否已进行isShow,避免爆堆栈
1295
+ * @returns 元素的外部宽度,单位为像素
1296
+ * @example
1297
+ * // 获取元素a.xx的外部宽度
1298
+ * DOMUtils.outerWidth(document.querySelector("a.xx"))
1299
+ * DOMUtils.outerWidth("a.xx")
1300
+ * > 100
1301
+ * // 获取window的外部宽度
1302
+ * DOMUtils.outerWidth(window)
1303
+ * > 400
1304
+ */
1305
+ outerWidth($el: HTMLElement | string | Window | typeof globalThis | Document, isShow?: boolean): number;
1306
+ outerWidth($el: HTMLElement | string | Window | typeof globalThis | Document, isShow: boolean = false): number {
1307
+ const that = this;
1308
+ if (CommonUtils.isWin($el)) {
1309
+ return that.windowApi.window.innerWidth;
1310
+ }
1311
+ if (typeof $el === "string") {
1312
+ $el = that.selector($el) as HTMLElement;
1313
+ }
1314
+ $el = $el as HTMLElement;
1315
+ if (isShow || (!isShow && CommonUtils.isShow($el))) {
1316
+ const style = that.windowApi.globalThis.getComputedStyle($el, null);
1317
+ const marginLeft = CommonUtils.getStyleValue(style, "marginLeft");
1318
+ const marginRight = CommonUtils.getStyleValue(style, "marginRight");
1319
+ return $el.offsetWidth + marginLeft + marginRight;
1320
+ } else {
1321
+ const { recovery } = CommonUtils.forceShow($el);
1322
+ const outerWidth = that.outerWidth($el, true);
1323
+ recovery();
1324
+ return outerWidth;
1325
+ }
1326
+ }
1327
+ /**
1328
+ * 获取元素的外部高度(包括边框和外边距)
1329
+ * @param {HTMLElement|string} $el 要获取外部高度的元素
1330
+ * @param {boolean} [isShow=false] 是否已进行isShow,避免爆堆栈
1331
+ * @returns {number} 元素的外部高度,单位为像素
1332
+ * @example
1333
+ * // 获取元素a.xx的外部高度
1334
+ * DOMUtils.outerHeight(document.querySelector("a.xx"))
1335
+ * DOMUtils.outerHeight("a.xx")
1336
+ * > 100
1337
+ * // 获取window的外部高度
1338
+ * DOMUtils.outerHeight(window)
1339
+ * > 700
1340
+ */
1341
+ outerHeight($el: HTMLElement | string | Window | typeof globalThis | Document, isShow?: boolean): number;
1342
+ outerHeight($el: HTMLElement | string | Window | typeof globalThis | Document, isShow: boolean = false): number {
1343
+ const that = this;
1344
+ if (CommonUtils.isWin($el)) {
1345
+ return that.windowApi.window.innerHeight;
1346
+ }
1347
+ if (typeof $el === "string") {
1348
+ $el = that.selector($el) as HTMLElement;
1349
+ }
1350
+ $el = $el as HTMLElement;
1351
+ if (isShow || (!isShow && CommonUtils.isShow($el))) {
1352
+ const style = that.windowApi.globalThis.getComputedStyle($el, null);
1353
+ const marginTop = CommonUtils.getStyleValue(style, "marginTop");
1354
+ const marginBottom = CommonUtils.getStyleValue(style, "marginBottom");
1355
+ return $el.offsetHeight + marginTop + marginBottom;
1356
+ } else {
1357
+ const { recovery } = CommonUtils.forceShow($el);
1358
+ const outerHeight = that.outerHeight($el, true);
1359
+ recovery();
1360
+ return outerHeight;
1361
+ }
1362
+ }
1363
+ /**
1364
+ * 将一个元素替换为另一个元素
1365
+ * @param $el 目标元素
1366
+ * @param $newEl 新元素
1367
+ * @example
1368
+ * // 替换元素a.xx为b.xx
1369
+ * DOMUtils.replaceWith(document.querySelector("a.xx"),document.querySelector("b.xx"))
1370
+ * DOMUtils.replaceWith("a.xx",'<b class="xx"></b>')
1371
+ */
1372
+ replaceWith($el: DOMUtilsTargetElementType, $newEl: HTMLElement | string | Node) {
1373
+ const that = this;
1374
+ if (typeof $el === "string") {
1375
+ $el = that.selectorAll($el);
1376
+ }
1377
+ if ($el == null) {
1378
+ return;
1379
+ }
1380
+ if (CommonUtils.isNodeList($el)) {
1381
+ // 设置
1382
+ $el.forEach(($elItem) => {
1383
+ that.replaceWith($elItem as HTMLElement, $newEl);
1384
+ });
1385
+ return;
1386
+ }
1387
+ if (typeof $newEl === "string") {
1388
+ $newEl = that.toElement($newEl, false, false);
1389
+ }
1390
+ const $parent = $el.parentElement;
1391
+ if ($parent) {
1392
+ $parent.replaceChild($newEl as Node, $el);
1393
+ } else {
1394
+ that.after($el, $newEl as HTMLElement);
1395
+ $el.remove();
1396
+ }
1397
+ }
1398
+ /**
1399
+ * 将一个元素包裹在指定的HTML元素中
1400
+ * @param $el 要包裹的元素
1401
+ * @param wrapperHTML 要包裹的HTML元素的字符串表示形式
1402
+ * @example
1403
+ * // 将a.xx元素外面包裹一层div
1404
+ * DOMUtils.wrap(document.querySelector("a.xx"),"<div></div>")
1405
+ */
1406
+ wrap($el: DOMUtilsTargetElementType, wrapperHTML: string) {
1407
+ const that = this;
1408
+ if (typeof $el === "string") {
1409
+ $el = that.selectorAll($el);
1410
+ }
1411
+ if ($el == null) {
1412
+ return;
1413
+ }
1414
+ if (CommonUtils.isNodeList($el)) {
1415
+ // 设置
1416
+ $el.forEach(($elItem) => {
1417
+ that.wrap($elItem as HTMLElement, wrapperHTML);
1418
+ });
1419
+ return;
1420
+ }
1421
+ $el = $el as HTMLElement;
1422
+ // 创建一个新的div元素,并将wrapperHTML作为其innerHTML
1423
+ const $wrapper = that.windowApi.document.createElement("div");
1424
+ that.html($wrapper, wrapperHTML);
1425
+
1426
+ const wrapperFirstChild = $wrapper.firstChild as HTMLElement;
1427
+ // 将要包裹的元素插入目标元素前面
1428
+ const parentElement = $el.parentElement as HTMLElement;
1429
+ parentElement.insertBefore(wrapperFirstChild, $el);
1430
+
1431
+ // 将要包裹的元素移动到wrapper中
1432
+ wrapperFirstChild.appendChild($el);
1433
+ }
1434
+ /**
1435
+ * 获取当前元素的前一个兄弟元素
1436
+ * @param $el 当前元素
1437
+ * @returns 前一个兄弟元素
1438
+ * @example
1439
+ * // 获取a.xx元素前一个兄弟元素
1440
+ * DOMUtils.prev(document.querySelector("a.xx"))
1441
+ * DOMUtils.prev("a.xx")
1442
+ * > <div ...>....</div>
1443
+ */
1444
+ prev($el: HTMLElement | string): HTMLElement;
1445
+ prev($el: HTMLElement | string) {
1446
+ const that = this;
1447
+ if (typeof $el === "string") {
1448
+ $el = that.selector($el) as HTMLElement;
1449
+ }
1450
+ if ($el == null) {
1451
+ return;
1452
+ }
1453
+ return $el.previousElementSibling as HTMLElement;
1454
+ }
1455
+ /**
1456
+ * 获取当前元素的后一个兄弟元素
1457
+ * @param $el 当前元素
1458
+ * @returns 后一个兄弟元素
1459
+ * @example
1460
+ * // 获取a.xx元素前一个兄弟元素
1461
+ * DOMUtils.next(document.querySelector("a.xx"))
1462
+ * DOMUtils.next("a.xx")
1463
+ * > <div ...>....</div>
1464
+ */
1465
+ next($el: HTMLElement | string): HTMLElement;
1466
+ next($el: HTMLElement | string) {
1467
+ const that = this;
1468
+ if (typeof $el === "string") {
1469
+ $el = that.selector($el) as HTMLElement;
1470
+ }
1471
+ if ($el == null) {
1472
+ return;
1473
+ }
1474
+ return $el.nextElementSibling as HTMLElement;
1475
+ }
1476
+ /**
1477
+ * 获取当前元素的所有兄弟元素
1478
+ * @param element 当前元素
1479
+ * @returns 所有兄弟元素
1480
+ * @example
1481
+ * // 获取a.xx元素所有兄弟元素
1482
+ * DOMUtils.siblings(document.querySelector("a.xx"))
1483
+ * DOMUtils.siblings("a.xx")
1484
+ * > (3)[div.logo-wrapper, div.forum-block, div.more-btn-desc]
1485
+ */
1486
+ siblings(element: HTMLElement | string): HTMLElement[];
1487
+ siblings($el: HTMLElement | string) {
1488
+ const that = this;
1489
+ if (typeof $el === "string") {
1490
+ $el = that.selector($el) as HTMLElement;
1491
+ }
1492
+ if ($el == null) {
1493
+ return;
1494
+ }
1495
+ return Array.from(($el.parentElement as HTMLElement).children as HTMLCollectionOf<HTMLElement>).filter(
1496
+ ($child) => $child !== $el
1497
+ );
1498
+ }
1499
+ /**
1500
+ * 获取当前元素的父元素
1501
+ * @param $el 当前元素
1502
+ * @returns 父元素
1503
+ * @example
1504
+ * // 获取a.xx元素的父元素
1505
+ * DOMUtils.parent(document.querySelector("a.xx"))
1506
+ * DOMUtils.parent("a.xx")
1507
+ * > <div ...>....</div>
1508
+ */
1509
+ parent($el: HTMLElement | string): HTMLElement;
1510
+ /**
1511
+ * 获取当前元素的父元素
1512
+ * @param $el 当前元素
1513
+ * @returns 父元素
1514
+ * @example
1515
+ * // 获取a.xx元素的父元素
1516
+ * DOMUtils.parent(document.querySelector("a.xx"))
1517
+ * DOMUtils.parent("a.xx")
1518
+ * > <div ...>....</div>
1519
+ */
1520
+ parent($el: HTMLElement[] | NodeList): HTMLElement[];
1521
+ /**
1522
+ * 获取当前元素的父元素
1523
+ * @param $el 当前元素
1524
+ * @returns 父元素
1525
+ * @example
1526
+ * // 获取a.xx元素的父元素
1527
+ * DOMUtils.parent(document.querySelector("a.xx"))
1528
+ * DOMUtils.parent("a.xx")
1529
+ * > <div ...>....</div>
1530
+ */
1531
+ parent($el: HTMLElement | Element | Node | NodeList | string | HTMLElement[]) {
1532
+ const that = this;
1533
+ if (typeof $el === "string") {
1534
+ $el = that.selector<HTMLElement>($el)!;
1535
+ }
1536
+ if ($el == null) {
1537
+ return;
1538
+ }
1539
+ if (CommonUtils.isNodeList($el)) {
1540
+ const resultArray: HTMLElement[] = [];
1541
+ $el.forEach(($elItem) => {
1542
+ resultArray.push(that.parent($elItem as HTMLElement));
1543
+ });
1544
+ return resultArray;
1545
+ } else {
1546
+ return $el.parentElement;
1547
+ }
1548
+ }
1549
+ /**
1550
+ * 将字符串转为Element元素
1551
+ * @param html
1552
+ * @param useParser 是否使用DOMParser来生成元素,有些时候通过DOMParser生成的元素有点问题
1553
+ * + true 使用DOMPraser来转换字符串
1554
+ * + false (默认)创建一个div,里面放入字符串,然后提取firstChild
1555
+ * @param isComplete 是否是完整的
1556
+ * + true 如果useParser为true,那么返回整个使用DOMParser转换成的Document
1557
+ * 如果useParser为false,返回一个DIV元素,DIV元素内包裹着需要转换的字符串
1558
+ * + false (默认)如果useParser为true,那么返回整个使用DOMParser转换成的Document的body
1559
+ * 如果useParser为false,返回一个DIV元素的firstChild
1560
+ * @example
1561
+ * // 将字符串转为Element元素
1562
+ * DOMUtils.toElement("<a href='xxxx'></a>")
1563
+ * > <a href="xxxx"></a>
1564
+ * @example
1565
+ * // 使用DOMParser将字符串转为Element元素
1566
+ * DOMUtils.toElement("<a href='xxxx'></a>",true)
1567
+ * > <a href="xxxx"></a>
1568
+ * @example
1569
+ * // 由于需要转换的元素是多个元素,将字符串转为完整的Element元素
1570
+ * DOMUtils.toElement("<a href='xxxx'></a><a href='xxxx'></a>",false, true)
1571
+ * > <div><a href="xxxx"></a><a href='xxxx'></a></div>
1572
+ * @example
1573
+ * // 由于需要转换的元素是多个元素,使用DOMParser将字符串转为完整的Element元素
1574
+ * DOMUtils.toElement("<a href='xxxx'></a><a href='xxxx'></a>",true, true)
1575
+ * > #document
1576
+ */
1577
+ toElement<T1 extends boolean, T2 extends boolean>(
1578
+ html: string,
1579
+ useParser?: T1,
1580
+ isComplete?: T2
1581
+ ): T1 extends true ? (T2 extends true ? Document : HTMLElement) : HTMLElement;
1582
+ toElement(html: string, useParser = false, isComplete = false) {
1583
+ const that = this;
1584
+ // 去除html前后的空格
1585
+ html = html.trim();
1586
+ function parseHTMLByDOMParser() {
1587
+ const parser = new DOMParser();
1588
+ if (isComplete) {
1589
+ return parser.parseFromString(html, "text/html");
1590
+ } else {
1591
+ return parser.parseFromString(html, "text/html").body.firstChild;
1592
+ }
1593
+ }
1594
+ function parseHTMLByCreateDom() {
1595
+ const $el = that.windowApi.document.createElement("div");
1596
+ that.html($el, html);
1597
+ if (isComplete) {
1598
+ return $el;
1599
+ } else {
1600
+ return $el.firstElementChild ?? $el.firstChild;
1601
+ }
1602
+ }
1603
+ if (useParser) {
1604
+ return parseHTMLByDOMParser();
1605
+ } else {
1606
+ return parseHTMLByCreateDom();
1607
+ }
1608
+ }
1609
+ /**
1610
+ * 将字符串转为Element元素数组
1611
+ * @param html
1612
+ * @param useParser 是否使用DOMParser来生成元素,有些时候通过DOMParser生成的元素有点问题
1613
+ * + true 使用DOMPraser来转换字符串
1614
+ * + false (默认)创建一个div,里面放入字符串,然后提取childNodes
1615
+ * @example
1616
+ * // 将字符串转为Element元素数组
1617
+ * DOMUtils.toElements("<a href='xxxx'></a>")
1618
+ * > [<a href="xxxx"></a>]
1619
+ * @example
1620
+ * // 使用DOMParser将字符串转为Element元素数组
1621
+ * DOMUtils.toElements("<a href='xxxx'></a>",true)
1622
+ * > [<a href="xxxx"></a>]
1623
+ */
1624
+ toElements(html: string, useParser = false) {
1625
+ const that = this;
1626
+ // 去除html前后的空格
1627
+ html = html.trim();
1628
+ function parseHTMLByDOMParser() {
1629
+ const parser = new DOMParser();
1630
+ return Array.from(parser.parseFromString(html, "text/html").body.childNodes);
1631
+ }
1632
+ function parseHTMLByCreateDom() {
1633
+ const $el = that.windowApi.document.createElement("div");
1634
+ that.html($el, html);
1635
+ return Array.from($el.childNodes);
1636
+ }
1637
+ if (useParser) {
1638
+ return parseHTMLByDOMParser();
1639
+ } else {
1640
+ return parseHTMLByCreateDom();
1641
+ }
1642
+ }
1643
+ /**
1644
+ * 序列化表单元素
1645
+ * @param $form 表单元素
1646
+ * @example
1647
+ * DOMUtils.serialize(document.querySelector("form"))
1648
+ * > xxx=xxx&aaa=
1649
+ */
1650
+ serialize($form: HTMLFormElement): string {
1651
+ if (!($form instanceof HTMLFormElement)) {
1652
+ throw new TypeError("DOMUtils.serialize 参数必须是HTMLFormElement");
1653
+ }
1654
+ const elements = $form.elements;
1655
+ const serializedArray: { name: string; value: string }[] = [];
1656
+
1657
+ for (let index = 0; index < elements.length; index++) {
1658
+ const $el = elements[index] as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
1659
+
1660
+ if (
1661
+ $el.name &&
1662
+ !$el.disabled &&
1663
+ (($el as HTMLInputElement).checked ||
1664
+ ["text", "hidden", "password", "textarea", "select-one", "select-multiple"].includes($el.type))
1665
+ ) {
1666
+ if ($el.type === "select-multiple") {
1667
+ for (let j = 0; j < ($el as HTMLSelectElement).options.length; j++) {
1668
+ if (($el as HTMLSelectElement).options[j].selected) {
1669
+ serializedArray.push({
1670
+ name: ($el as HTMLSelectElement).name,
1671
+ value: ($el as HTMLSelectElement).options[j].value,
1672
+ });
1673
+ }
1674
+ }
1675
+ } else {
1676
+ serializedArray.push({ name: $el.name, value: $el.value });
1677
+ }
1678
+ }
1679
+ }
1680
+
1681
+ return serializedArray
1682
+ .map((item) => `${encodeURIComponent(item.name)}=${encodeURIComponent(item.value)}`)
1683
+ .join("&");
1684
+ }
1685
+ /**
1686
+ * 创建一个新的DOMUtils实例
1687
+ * @param option
1688
+ * @returns
1689
+ */
1690
+ createDOMUtils(option?: WindowApiOption) {
1691
+ return new DOMUtils(option);
1692
+ }
1693
+ /**
1694
+ * 获取文字的位置信息
1695
+ * @param $input 输入框
1696
+ * @param selectionStart 起始位置
1697
+ * @param selectionEnd 结束位置
1698
+ * @example
1699
+ * DOMUtils.getTextBoundingRect(document.querySelector("input"));
1700
+ */
1701
+ getTextBoundingRect(
1702
+ $input: HTMLInputElement,
1703
+ selectionStart?: number | string,
1704
+ selectionEnd?: number | string
1705
+ ): DOMRect {
1706
+ const that = this;
1707
+ // Basic parameter validation
1708
+ if (!$input || !("value" in $input)) return $input;
1709
+ if (selectionStart == null) {
1710
+ selectionStart = $input.selectionStart || 0;
1711
+ }
1712
+ if (selectionEnd == null) {
1713
+ selectionEnd = $input.selectionEnd || 0;
1714
+ }
1715
+ if (typeof selectionStart == "string") selectionStart = parseFloat(selectionStart);
1716
+ if (typeof selectionStart != "number" || isNaN(selectionStart)) {
1717
+ selectionStart = 0;
1718
+ }
1719
+ if (selectionStart < 0) selectionStart = 0;
1720
+ else selectionStart = Math.min($input.value.length, selectionStart);
1721
+ if (typeof selectionEnd == "string") selectionEnd = parseFloat(selectionEnd);
1722
+ if (typeof selectionEnd != "number" || isNaN(selectionEnd) || selectionEnd < selectionStart) {
1723
+ selectionEnd = selectionStart;
1724
+ }
1725
+ if (selectionEnd < 0) selectionEnd = 0;
1726
+ else selectionEnd = Math.min($input.value.length, selectionEnd);
1727
+
1728
+ // If available (thus IE), use the createTextRange method
1729
+ if (typeof (<any>$input).createTextRange == "function") {
1730
+ const range = ($input as any).createTextRange();
1731
+ range.collapse(true);
1732
+ range.moveStart("character", selectionStart);
1733
+ range.moveEnd("character", selectionEnd - selectionStart);
1734
+ return range.getBoundingClientRect();
1735
+ }
1736
+ // createTextRange is not supported, create a fake text range
1737
+ const offset = getInputOffset(),
1738
+ width = getInputCSS("width", true),
1739
+ height = getInputCSS("height", true);
1740
+ let topPos = offset.top;
1741
+ let leftPos = offset.left;
1742
+
1743
+ // Styles to simulate a node in an input field
1744
+ let cssDefaultStyles = "white-space:pre;padding:0;margin:0;";
1745
+ const listOfModifiers = [
1746
+ "direction",
1747
+ "font-family",
1748
+ "font-size",
1749
+ "font-size-adjust",
1750
+ "font-variant",
1751
+ "font-weight",
1752
+ "font-style",
1753
+ "letter-spacing",
1754
+ "line-height",
1755
+ "text-align",
1756
+ "text-indent",
1757
+ "text-transform",
1758
+ "word-wrap",
1759
+ "word-spacing",
1760
+ ];
1761
+ topPos += getInputCSS("padding-top", true) as number;
1762
+ topPos += getInputCSS("border-top-width", true) as number;
1763
+ leftPos += getInputCSS("padding-left", true) as number;
1764
+ leftPos += getInputCSS("border-left-width", true) as number;
1765
+ leftPos += 1; //Seems to be necessary
1766
+
1767
+ for (let index = 0; index < listOfModifiers.length; index++) {
1768
+ const property = listOfModifiers[index];
1769
+ cssDefaultStyles += property + ":" + getInputCSS(property, false) + ";";
1770
+ }
1771
+ // End of CSS variable checks
1772
+ // 不能为空,不然获取不到高度
1773
+ const text = $input.value || "G",
1774
+ textLen = text.length,
1775
+ fakeClone = that.windowApi.document.createElement("div");
1776
+ if (selectionStart > 0) appendPart(0, selectionStart);
1777
+ const fakeRange = appendPart(selectionStart, selectionEnd);
1778
+ if (textLen > selectionEnd) appendPart(selectionEnd, textLen);
1779
+
1780
+ // Styles to inherit the font styles of the element
1781
+ fakeClone.style.cssText = cssDefaultStyles;
1782
+
1783
+ // Styles to position the text node at the desired position
1784
+ fakeClone.style.position = "absolute";
1785
+ fakeClone.style.top = topPos + "px";
1786
+ fakeClone.style.left = leftPos + "px";
1787
+ fakeClone.style.width = width + "px";
1788
+ fakeClone.style.height = height + "px";
1789
+ that.windowApi.document.body.appendChild(fakeClone);
1790
+ const returnValue = fakeRange.getBoundingClientRect(); //Get rect
1791
+
1792
+ fakeClone?.parentNode?.removeChild(fakeClone); //Remove temp
1793
+ return returnValue;
1794
+
1795
+ // Local functions for readability of the previous code
1796
+ /**
1797
+ *
1798
+ * @param start
1799
+ * @param end
1800
+ * @returns
1801
+ */
1802
+ function appendPart(start: number, end: number) {
1803
+ const span = that.windowApi.document.createElement("span");
1804
+ span.style.cssText = cssDefaultStyles; //Force styles to prevent unexpected results
1805
+ span.textContent = text.substring(start, end);
1806
+ fakeClone.appendChild(span);
1807
+ return span;
1808
+ }
1809
+ // Computing offset position
1810
+ function getInputOffset() {
1811
+ const body = that.windowApi.document.body,
1812
+ win = that.windowApi.document.defaultView!,
1813
+ docElem = that.windowApi.document.documentElement,
1814
+ $box = that.windowApi.document.createElement("div");
1815
+ $box.style.paddingLeft = $box.style.width = "1px";
1816
+ body.appendChild($box);
1817
+ const isBoxModel = $box.offsetWidth == 2;
1818
+ body.removeChild($box);
1819
+ const $boxRect = $input.getBoundingClientRect();
1820
+ const clientTop = docElem.clientTop || body.clientTop || 0,
1821
+ clientLeft = docElem.clientLeft || body.clientLeft || 0,
1822
+ scrollTop = win.pageYOffset || (isBoxModel && docElem.scrollTop) || body.scrollTop,
1823
+ scrollLeft = win.pageXOffset || (isBoxModel && docElem.scrollLeft) || body.scrollLeft;
1824
+ return {
1825
+ top: $boxRect.top + scrollTop - clientTop,
1826
+ left: $boxRect.left + scrollLeft - clientLeft,
1827
+ };
1828
+ }
1829
+ /**
1830
+ *
1831
+ * @param prop
1832
+ * @param isNumber
1833
+ * @returns
1834
+ */
1835
+ function getInputCSS<T extends boolean>(prop: string, isNumber: T): T extends true ? number : string {
1836
+ const val = that.windowApi.document.defaultView!.getComputedStyle($input, null).getPropertyValue(prop);
1837
+ if (isNumber) {
1838
+ return parseFloat(val) as T extends true ? number : string;
1839
+ } else {
1840
+ return val as T extends true ? number : string;
1841
+ }
1842
+ }
1843
+ }
1844
+ /**
1845
+ * 在页面中增加style元素,如果html节点存在子节点,添加子节点第一个,反之,添加到html节点的子节点最后一个
1846
+ * @param cssText css字符串
1847
+ * @returns 返回添加的CSS标签
1848
+ * @example
1849
+ * DOMUtils.addStyle("html{}");
1850
+ * > <style type="text/css">html{}</style>
1851
+ */
1852
+ addStyle(cssText: string): HTMLStyleElement;
1853
+ addStyle(cssText: string): HTMLStyleElement {
1854
+ if (typeof cssText !== "string") {
1855
+ throw new Error("DOMUtils.addStyle 参数cssText 必须为String类型");
1856
+ }
1857
+ const $css = this.createElement("style", {
1858
+ type: "text/css",
1859
+ innerHTML: cssText,
1860
+ });
1861
+ if (this.windowApi.document.head) {
1862
+ /* 插入head最后 */
1863
+ this.windowApi.document.head.appendChild($css);
1864
+ } else if (this.windowApi.document.documentElement.childNodes.length === 0) {
1865
+ /* 插入#html后 */
1866
+ this.windowApi.document.documentElement.appendChild($css);
1867
+ } else {
1868
+ /* 插入#html第一个元素前 */
1869
+ this.windowApi.document.documentElement.insertBefore($css, this.windowApi.document.documentElement.childNodes[0]);
1870
+ }
1871
+ return $css;
1872
+ }
1873
+ /**
1874
+ * 检测点击的地方是否在该元素区域内
1875
+ * @param $el 需要检测的元素
1876
+ * @returns
1877
+ * + true 点击在元素上
1878
+ * + false 未点击在元素上
1879
+ * @example
1880
+ * DOMUtils.checkUserClickInNode(document.querySelector(".xxx"));
1881
+ * > false
1882
+ **/
1883
+ checkUserClickInNode($el: Element | Node | HTMLElement) {
1884
+ const that = this;
1885
+ if (!CommonUtils.isDOM($el)) {
1886
+ throw new Error("DOMUtils.checkUserClickInNode 参数 targetNode 必须为 Element|Node 类型");
1887
+ }
1888
+ const clickEvent = that.windowApi.window.event as PointerEvent;
1889
+ const touchEvent = that.windowApi.window.event as TouchEvent;
1890
+ const $click = clickEvent?.composedPath()?.[0] as HTMLElement;
1891
+
1892
+ // 点击的x坐标
1893
+ const clickPosX = clickEvent?.clientX != null ? clickEvent.clientX : touchEvent.touches[0].clientX;
1894
+ // 点击的y坐标
1895
+ const clickPosY = clickEvent?.clientY != null ? clickEvent.clientY : touchEvent.touches[0].clientY;
1896
+ const {
1897
+ /* 要检测的元素的相对屏幕的横坐标最左边 */
1898
+ left: elementPosXLeft,
1899
+ /* 要检测的元素的相对屏幕的横坐标最右边 */
1900
+ right: elementPosXRight,
1901
+ /* 要检测的元素的相对屏幕的纵坐标最上边 */
1902
+ top: elementPosYTop,
1903
+ /* 要检测的元素的相对屏幕的纵坐标最下边 */
1904
+ bottom: elementPosYBottom,
1905
+ } = (<HTMLElement>$el).getBoundingClientRect();
1906
+ if (
1907
+ clickPosX >= elementPosXLeft &&
1908
+ clickPosX <= elementPosXRight &&
1909
+ clickPosY >= elementPosYTop &&
1910
+ clickPosY <= elementPosYBottom
1911
+ ) {
1912
+ return true;
1913
+ } else if (($click && $el.contains($click)) || $click == $el) {
1914
+ /* 这种情况是应对在界面中隐藏的元素,getBoundingClientRect获取的都是0 */
1915
+ return true;
1916
+ } else {
1917
+ return false;
1918
+ }
1919
+ }
1920
+ /**
1921
+ * 删除某个父元素,父元素可能在上层或上上层或上上上层...
1922
+ * @param $el 当前元素
1923
+ * @param parentSelector 判断是否满足父元素,参数为当前处理的父元素,满足返回true,否则false
1924
+ * @returns
1925
+ * + true 已删除
1926
+ * + false 未删除
1927
+ * @example
1928
+ * DOMUtils.deleteParentNode(document.querySelector("a"),".xxx");
1929
+ * > true
1930
+ **/
1931
+ deleteParentNode($el: Node | HTMLElement | Element | null, parentSelector: string): boolean;
1932
+ deleteParentNode($el: Node | HTMLElement | Element | null, parentSelector: string) {
1933
+ if ($el == null) {
1934
+ return;
1935
+ }
1936
+ if (!CommonUtils.isDOM($el)) {
1937
+ throw new Error("DOMUtils.deleteParentNode 参数 target 必须为 Node|HTMLElement 类型");
1938
+ }
1939
+ if (typeof parentSelector !== "string") {
1940
+ throw new Error("DOMUtils.deleteParentNode 参数 targetSelector 必须为 string 类型");
1941
+ }
1942
+ let result = false;
1943
+ const $parent = domUtils.closest($el as HTMLElement, parentSelector);
1944
+ if ($parent) {
1945
+ this.remove($parent);
1946
+ result = true;
1947
+ }
1948
+ return result;
1949
+ }
1950
+ /**
1951
+ * 定位元素上的字符串,返回一个迭代器
1952
+ * @param $el 目标元素
1953
+ * @param text 需要定位的字符串
1954
+ * @param filter (可选)过滤器函数,返回值为true是排除该元素
1955
+ * @example
1956
+ * let textIterator = DOMUtils.findElementsWithText(document.documentElement,"xxxx");
1957
+ * textIterator.next();
1958
+ * > {value: ?HTMLElement, done: boolean, next: Function}
1959
+ */
1960
+ findElementsWithText<T extends HTMLElement | Element | Node>(
1961
+ $el: T,
1962
+ text: string,
1963
+ filter?: (element: T) => boolean
1964
+ ): Generator<HTMLElement | ChildNode, void, any>;
1965
+ *findElementsWithText<T extends HTMLElement | Element | Node>(
1966
+ $el: T,
1967
+ text: string,
1968
+ filter?: (element: T) => boolean
1969
+ ) {
1970
+ const that = this;
1971
+ if ((<HTMLElement>$el).outerHTML.includes(text)) {
1972
+ if ((<HTMLElement>$el).children.length === 0) {
1973
+ const filterResult = typeof filter === "function" ? filter($el) : false;
1974
+ if (!filterResult) {
1975
+ yield $el as any;
1976
+ }
1977
+ } else {
1978
+ const $text = Array.from($el.childNodes).filter(($child) => $child.nodeType === Node.TEXT_NODE);
1979
+ for (const $child of $text) {
1980
+ if ((<HTMLElement>$child).textContent.includes(text)) {
1981
+ const filterResult = typeof filter === "function" ? filter($el) : false;
1982
+ if (!filterResult) {
1983
+ yield $child;
1984
+ }
1985
+ }
1986
+ }
1987
+ }
1988
+ }
1989
+
1990
+ for (let index = 0; index < (<HTMLElement>$el).children.length; index++) {
1991
+ const $child = (<HTMLElement>$el).children[index] as T;
1992
+ yield* that.findElementsWithText($child, text, filter);
1993
+ }
1994
+ }
1995
+ /**
1996
+ * 寻找可见元素,如果元素不可见,则向上找它的父元素直至找到,如果父元素不存在则返回null
1997
+ * @param $el
1998
+ * @example
1999
+ * let visibleElement = DOMUtils.findVisibleElement(document.querySelector("a.xx"));
2000
+ * > <HTMLElement>
2001
+ */
2002
+ findVisibleElement($el: HTMLElement | Element | Node) {
2003
+ let $current = $el as HTMLElement;
2004
+ while ($current) {
2005
+ const rect = $current.getBoundingClientRect();
2006
+ if ((rect as any).length) {
2007
+ return $current;
2008
+ }
2009
+ $current = $current.parentElement as HTMLElement;
2010
+ }
2011
+ return null;
2012
+ }
2013
+ /**
2014
+ * 将元素上的文本或元素使用光标进行选中
2015
+ *
2016
+ * 注意,如果设置startIndex和endIndex,且元素上并无可选则的坐标,那么会报错
2017
+ * @param $el 目标元素
2018
+ * @param childTextNode 目标元素下的#text元素
2019
+ * @param startIndex (可选)开始坐标,可为空
2020
+ * @param endIndex (可选)结束坐标,可为空
2021
+ * @example
2022
+ * DOMUtils.setElementSelection(document.querySelector("span"));
2023
+ */
2024
+ setElementSelection(
2025
+ $el: HTMLElement | Element | Node,
2026
+ childTextNode?: ChildNode,
2027
+ startIndex?: number,
2028
+ endIndex?: number
2029
+ ): void {
2030
+ const range = this.windowApi.document.createRange();
2031
+ range.selectNodeContents($el);
2032
+ if (childTextNode) {
2033
+ if (childTextNode.nodeType !== Node.TEXT_NODE) {
2034
+ throw new TypeError("childTextNode必须是#text元素");
2035
+ }
2036
+ if (startIndex != null && endIndex != null) {
2037
+ range.setStart(childTextNode, startIndex);
2038
+ range.setEnd(childTextNode, endIndex);
2039
+ }
2040
+ }
2041
+
2042
+ const selection = this.windowApi.globalThis.getSelection();
2043
+ if (selection) {
2044
+ selection.removeAllRanges();
2045
+ selection.addRange(range);
2046
+ }
2047
+ }
2048
+ }
2049
+
2050
+ const domUtils = new DOMUtils();
2051
+
2052
+ export { domUtils as DOMUtils };