@whitesev/domutils 1.1.5 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1705 @@
1
+ import { DOMUtilsCommonUtils } from "./DOMUtilsCommonUtils";
2
+ import { DOMUtilsCore } from "./DOMUtilsCore";
3
+ import type { DOMUtilsCoreOption } from "./DOMUtilsCore";
4
+ import {
5
+ type DOMUtilsCreateElementAttributesMap,
6
+ DOMUtilsEvent,
7
+ } from "./DOMUtilsEvent";
8
+ import { ParseHTMLReturnType } from "./types/global";
9
+
10
+ class DOMUtils extends DOMUtilsEvent {
11
+ constructor(option?: DOMUtilsCoreOption) {
12
+ DOMUtilsCore.init(option);
13
+ super();
14
+ }
15
+ /** 版本号 */
16
+ version = "2024.6.21";
17
+ /**
18
+ * 获取元素的属性值
19
+ * @param element 目标元素
20
+ * @param attrName 属性名
21
+ * @example
22
+ * // 获取a.xx元素的href属性
23
+ * DOMUtils.attr(document.querySelector("a.xx"),"href");
24
+ * DOMUtils.attr("a.xx","href");
25
+ * > https://xxxx....
26
+ */
27
+ attr(element: HTMLElement | string, attrName: string): string;
28
+ /**
29
+ * 设置元素的属性值
30
+ * @param element 目标元素
31
+ * @param attrName 属性名
32
+ * @param attrValue 属性值
33
+ * @example
34
+ * // 修改a.xx元素的href属性为abcd
35
+ * DOMUtils.attr(document.querySelector("a.xx"),"href","abcd");
36
+ * DOMUtils.attr("a.xx","href","abcd");
37
+ */
38
+ attr(
39
+ element: HTMLElement | string,
40
+ attrName: string,
41
+ attrValue: string
42
+ ): void;
43
+ attr(element: HTMLElement | string, attrName: string, attrValue?: string) {
44
+ if (typeof element === "string") {
45
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
46
+ }
47
+ if (element == null) {
48
+ return;
49
+ }
50
+ if (attrValue == null) {
51
+ return element.getAttribute(attrName);
52
+ } else {
53
+ element.setAttribute(attrName, attrValue);
54
+ }
55
+ }
56
+ /**
57
+ * 创建元素
58
+ * @param tagName 标签名
59
+ * @param property 属性
60
+ * @param attributes 元素上的自定义属性
61
+ * @example
62
+ * // 创建一个DIV元素,且属性class为xxx
63
+ * DOMUtils.createElement("div",undefined,{ class:"xxx" });
64
+ * > <div class="xxx"></div>
65
+ * @example
66
+ * // 创建一个DIV元素
67
+ * DOMUtils.createElement("div");
68
+ * > <div></div>
69
+ * @example
70
+ * // 创建一个DIV元素
71
+ * DOMUtils.createElement("div","测试");
72
+ * > <div>测试</div>
73
+ */
74
+ createElement<K extends keyof HTMLElementTagNameMap>(
75
+ /** 元素名 */
76
+ tagName: K,
77
+ /** 属性 */
78
+ property?:
79
+ | ({
80
+ [P in keyof HTMLElementTagNameMap[K]]?: HTMLElementTagNameMap[K][P];
81
+ } & {
82
+ [key: string]: any;
83
+ })
84
+ | string,
85
+ /** 自定义属性 */
86
+ attributes?: DOMUtilsCreateElementAttributesMap
87
+ ): HTMLElementTagNameMap[K] {
88
+ let tempElement = DOMUtilsCore.document.createElement(tagName);
89
+ if (typeof property === "string") {
90
+ tempElement.innerHTML = property;
91
+ return tempElement;
92
+ }
93
+ if (property == null) {
94
+ property = {};
95
+ }
96
+ if (attributes == null) {
97
+ attributes = {};
98
+ }
99
+ Object.keys(property).forEach((key) => {
100
+ let value = property[key];
101
+ (tempElement as any)[key] = value;
102
+ });
103
+ Object.keys(attributes).forEach((key) => {
104
+ let value = attributes[key];
105
+ if (typeof value === "object") {
106
+ /* object转字符串 */
107
+ value = JSON.stringify(value);
108
+ } else if (typeof value === "function") {
109
+ /* function转字符串 */
110
+ value = value.toString();
111
+ }
112
+ tempElement.setAttribute(key, value);
113
+ });
114
+ return tempElement;
115
+ }
116
+
117
+ /**
118
+ * 获取元素的样式属性值
119
+ * @param element 目标元素
120
+ * @param property 样式属性名或包含多个属性名和属性值的对象
121
+ * @example
122
+ * // 获取元素a.xx的CSS属性display
123
+ * DOMUtils.css(document.querySelector("a.xx"),"display");
124
+ * DOMUtils.css("a.xx","display");
125
+ * > "none"
126
+ * */
127
+ css(
128
+ element: HTMLElement | string,
129
+ property: keyof CSSStyleDeclaration
130
+ ): string;
131
+ /**
132
+ * 获取元素的样式属性值
133
+ * @param element 目标元素
134
+ * @param property 样式属性名或包含多个属性名和属性值的对象
135
+ * @example
136
+ * // 获取元素a.xx的CSS属性display
137
+ * DOMUtils.css(document.querySelector("a.xx"),"display");
138
+ * DOMUtils.css("a.xx","display");
139
+ * > "none"
140
+ * */
141
+ css(element: HTMLElement | string, property: string): string;
142
+ /**
143
+ * 设置元素的样式属性
144
+ * @param element 目标元素
145
+ * @param property 样式属性名或包含多个属性名和属性值的对象
146
+ * @param value 样式属性值
147
+ * @example
148
+ * // 设置元素a.xx的CSS属性display为block
149
+ * DOMUtils.css(document.querySelector("a.xx"),"display","block");
150
+ * DOMUtils.css(document.querySelector("a.xx"),"display","block !important");
151
+ * DOMUtils.css("a.xx","display","block");
152
+ * DOMUtils.css("a.xx","display","block !important");
153
+ * @example
154
+ * // 设置元素a.xx的CSS属性top为10px
155
+ * DOMUtils.css(document.querySelector("a.xx"),"top","10px");
156
+ * DOMUtils.css(document.querySelector("a.xx"),"top",10);
157
+ * */
158
+ css(
159
+ element: HTMLElement | string,
160
+ property: keyof CSSStyleDeclaration & string,
161
+ value: string | number
162
+ ): string;
163
+ /**
164
+ * 设置元素的样式属性
165
+ * @param element 目标元素
166
+ * @param property 样式属性名或包含多个属性名和属性值的对象
167
+ * @param value 样式属性值
168
+ * @example
169
+ * // 设置元素a.xx的CSS属性display为block
170
+ * DOMUtils.css(document.querySelector("a.xx"),{ display: "block" }});
171
+ * DOMUtils.css(document.querySelector("a.xx"),{ display: "block !important" }});
172
+ * @example
173
+ * // 设置元素a.xx的CSS属性top为10px
174
+ * DOMUtils.css(document.querySelector("a.xx"),{ top: "10px" });
175
+ * DOMUtils.css(document.querySelector("a.xx"),{ top: 10 });
176
+ * */
177
+ css(
178
+ element: HTMLElement | string,
179
+ property:
180
+ | {
181
+ [P in keyof CSSStyleDeclaration]?: CSSStyleDeclaration[P];
182
+ }
183
+ | {
184
+ [key: string]: string | number;
185
+ }
186
+ ): string;
187
+ css(
188
+ element: HTMLElement | string,
189
+ property:
190
+ | keyof CSSStyleDeclaration
191
+ | string
192
+ | {
193
+ [P in keyof CSSStyleDeclaration]?:
194
+ | string
195
+ | number
196
+ | CSSStyleDeclaration[P];
197
+ },
198
+ value?: string | number
199
+ ) {
200
+ /**
201
+ * 把纯数字没有px的加上
202
+ */
203
+ function handlePixe(propertyName: string, propertyValue: string | number) {
204
+ let allowAddPixe = [
205
+ "width",
206
+ "height",
207
+ "top",
208
+ "left",
209
+ "right",
210
+ "bottom",
211
+ "font-size",
212
+ ];
213
+ if (typeof propertyValue === "number") {
214
+ propertyValue = propertyValue.toString();
215
+ }
216
+ if (
217
+ typeof propertyValue === "string" &&
218
+ allowAddPixe.includes(propertyName) &&
219
+ propertyValue.match(/[0-9]$/gi)
220
+ ) {
221
+ propertyValue = propertyValue + "px";
222
+ }
223
+ return propertyValue;
224
+ }
225
+ if (typeof element === "string") {
226
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
227
+ }
228
+ if (element == null) {
229
+ return;
230
+ }
231
+ if (typeof property === "string") {
232
+ if (value == null) {
233
+ return getComputedStyle(element).getPropertyValue(property);
234
+ } else {
235
+ if (value === "string" && value.includes("!important")) {
236
+ element.style.setProperty(property, value, "important");
237
+ } else {
238
+ value = handlePixe(property, value);
239
+ element.style.setProperty(property, value);
240
+ }
241
+ }
242
+ } else if (typeof property === "object") {
243
+ for (let prop in property) {
244
+ if (
245
+ typeof property[prop] === "string" &&
246
+ (property[prop] as string).includes("!important")
247
+ ) {
248
+ element.style.setProperty(
249
+ prop,
250
+ property[prop] as string,
251
+ "important"
252
+ );
253
+ } else {
254
+ property[prop] = handlePixe(prop, property[prop] as string);
255
+ element.style.setProperty(prop, property[prop] as string);
256
+ }
257
+ }
258
+ }
259
+ }
260
+ /**
261
+ * 获取元素的文本内容
262
+ * @param element 目标元素
263
+ * @param text (可选)文本内容
264
+ * @returns 如果传入了text,则返回undefined;否则返回文本内容
265
+ * @example
266
+ * // 设置元素a.xx的文本内容为abcd
267
+ * DOMUtils.text(document.querySelector("a.xx"),"abcd")
268
+ * DOMUtils.text("a.xx","abcd")
269
+ * DOMUtils.text("a.xx",document.querySelector("b"))
270
+ * */
271
+ text(element: HTMLElement | string): string;
272
+ /**
273
+ * 设置元素的文本内容
274
+ * @param element 目标元素
275
+ * @param text (可选)文本内容
276
+ * @returns 如果传入了text,则返回undefined;否则返回文本内容
277
+ * @example
278
+ * // 设置元素a.xx的文本内容为abcd
279
+ * DOMUtils.text(document.querySelector("a.xx"),"abcd")
280
+ * DOMUtils.text("a.xx","abcd")
281
+ * DOMUtils.text("a.xx",document.querySelector("b"))
282
+ * */
283
+ text(
284
+ element: HTMLElement | string,
285
+ text: string | HTMLElement | Element
286
+ ): void;
287
+ text(element: HTMLElement | string, text?: string | HTMLElement | Element) {
288
+ if (typeof element === "string") {
289
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
290
+ }
291
+ if (element == null) {
292
+ return;
293
+ }
294
+ if (text == null) {
295
+ return element.textContent || element.innerText;
296
+ } else {
297
+ if (text instanceof Node) {
298
+ text = text.textContent || (text as HTMLElement).innerText;
299
+ }
300
+ if ("textContent" in element) {
301
+ element.textContent = text as string;
302
+ } else if ("innerText" in element) {
303
+ (element as HTMLElement).innerText = text as string;
304
+ }
305
+ }
306
+ }
307
+
308
+ /**
309
+ * 设置元素的HTML内容
310
+ * @param element 目标元素
311
+ * @param html (可选)HTML内容|元素
312
+ * @returns 如果传入了html,则返回undefined;否则返回HTML内容
313
+ * @example
314
+ * // 设置元素a.xx的文本内容为<b>abcd</b>
315
+ * DOMUtils.html(document.querySelector("a.xx"),"<b>abcd</b>")
316
+ * DOMUtils.html("a.xx","<b>abcd</b>")
317
+ * DOMUtils.html("a.xx",document.querySelector("b"))
318
+ * */
319
+ html(
320
+ element: HTMLElement | string,
321
+ html: string | HTMLElement | Element
322
+ ): void;
323
+ /**
324
+ * 获取元素的HTML内容
325
+ * @param element 目标元素
326
+ * @param html (可选)HTML内容|元素
327
+ * @returns 如果传入了html,则返回undefined;否则返回HTML内容
328
+ * @example
329
+ * // 设置元素a.xx的文本内容为<b>abcd</b>
330
+ * DOMUtils.html(document.querySelector("a.xx"),"<b>abcd</b>")
331
+ * DOMUtils.html("a.xx","<b>abcd</b>")
332
+ * DOMUtils.html("a.xx",document.querySelector("b"))
333
+ * */
334
+ html(element: HTMLElement | string): string;
335
+ html(element: HTMLElement | string, html?: string | HTMLElement | Element) {
336
+ if (typeof element === "string") {
337
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
338
+ }
339
+ if (element == null) {
340
+ return;
341
+ }
342
+ if (html == null) {
343
+ return element.innerHTML;
344
+ } else {
345
+ if (html instanceof Node) {
346
+ html = html.innerHTML;
347
+ }
348
+ if ("innerHTML" in element) {
349
+ element.innerHTML = html;
350
+ }
351
+ }
352
+ }
353
+ /**
354
+ * 获取移动元素的transform偏移
355
+ */
356
+ getTransform(
357
+ element: HTMLElement,
358
+ isShow: boolean = false
359
+ ): {
360
+ transformLeft: number;
361
+ transformTop: number;
362
+ } {
363
+ let DOMUtilsContext = this;
364
+ let transform_left = 0;
365
+ let transform_top = 0;
366
+ if (!(isShow || (!isShow && DOMUtilsCommonUtils.isShow(element)))) {
367
+ /* 未显示 */
368
+ let { recovery } = DOMUtilsCommonUtils.showElement(element);
369
+ let transformInfo = DOMUtilsContext.getTransform(element, true);
370
+ recovery();
371
+ return transformInfo;
372
+ }
373
+ let elementTransform = getComputedStyle(element).transform;
374
+ if (
375
+ elementTransform != null &&
376
+ elementTransform !== "none" &&
377
+ elementTransform !== ""
378
+ ) {
379
+ let elementTransformSplit = elementTransform
380
+ .match(/\((.+)\)/)?.[1]
381
+ .split(",");
382
+ if (elementTransformSplit) {
383
+ transform_left = Math.abs(parseInt(elementTransformSplit[4]));
384
+ transform_top = Math.abs(parseInt(elementTransformSplit[5]));
385
+ } else {
386
+ transform_left = 0;
387
+ transform_top = 0;
388
+ }
389
+ }
390
+ return {
391
+ transformLeft: transform_left,
392
+ transformTop: transform_top,
393
+ };
394
+ }
395
+
396
+ /**
397
+ * 设置元素的value属性值
398
+ * @param element 目标元素
399
+ * @param value (可选)value属性值
400
+ * @returns 如果传入了value,则返回undefined;否则返回value属性值
401
+ * > true
402
+ * @example
403
+ * // 修改元素input.xx的复选框值为true
404
+ * DOMUtils.val(document.querySelector("input.xx"),true)
405
+ * DOMUtils.val("input.xx",true)
406
+ * */
407
+ val(element: HTMLInputElement | string, value: string | boolean): void;
408
+ /**
409
+ * 获取value属性值
410
+ * @param element 目标元素
411
+ * @example
412
+ * // 获取元素textarea的值
413
+ * DOMUtils.val(document.querySelector("textarea.xx"))
414
+ * */
415
+ val(element: HTMLInputElement | string): string;
416
+ /**
417
+ * 获取value属性值
418
+ * @param element 目标元素
419
+ * @example
420
+ * // 获取元素input.xx的复选框值
421
+ * DOMUtils.val(document.querySelector("input.xx"))
422
+ * DOMUtils.val("input.xx")
423
+ * */
424
+ val(element: HTMLInputElement): boolean | string;
425
+ val(element: HTMLInputElement | string, value?: string | boolean) {
426
+ if (typeof element === "string") {
427
+ element = DOMUtilsCore.document.querySelector(
428
+ element
429
+ ) as HTMLInputElement;
430
+ }
431
+ if (element == null) {
432
+ return;
433
+ }
434
+ if (value == null) {
435
+ if (
436
+ element.localName === "input" &&
437
+ (element.type === "checkbox" || element.type === "radio")
438
+ ) {
439
+ return element.checked;
440
+ } else {
441
+ return element.value;
442
+ }
443
+ } else {
444
+ if (
445
+ element.localName === "input" &&
446
+ (element.type === "checkbox" || element.type === "radio")
447
+ ) {
448
+ element.checked = !!value;
449
+ } else {
450
+ element.value = value as string;
451
+ }
452
+ }
453
+ }
454
+
455
+ /**
456
+ * 获取元素的属性值
457
+ * @param element 目标元素
458
+ * @param propName 属性名
459
+ * @param propValue 属性值
460
+ * @example
461
+ * // 获取元素a.xx的属性data-value
462
+ * DOMUtils.val(document.querySelector("a.xx"),"data-value")
463
+ * DOMUtils.val("a.xx","data-value")
464
+ * > undefined
465
+ * */
466
+ prop<T extends any>(element: HTMLElement | string, propName: string): T;
467
+ /**
468
+ * 设置元素的属性值
469
+ * @param element 目标元素
470
+ * @param propName 属性名
471
+ * @param propValue 属性值
472
+ * @example
473
+ * // 设置元素a.xx的属性data-value为1
474
+ * DOMUtils.val(document.querySelector("a.xx"),"data-value",1)
475
+ * DOMUtils.val("a.xx","data-value",1)
476
+ * */
477
+ prop<T extends any>(
478
+ element: HTMLElement | string,
479
+ propName: string,
480
+ propValue: T
481
+ ): void;
482
+ prop(element: HTMLElement | string, propName: string, propValue?: any) {
483
+ if (element == null) {
484
+ return;
485
+ }
486
+ if (typeof element === "string") {
487
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
488
+ }
489
+ if (propValue == null) {
490
+ return (element as any)[propName];
491
+ } else {
492
+ (element as any)[propName] = propValue;
493
+ }
494
+ }
495
+ /**
496
+ * 移除元素的属性
497
+ * @param element 目标元素
498
+ * @param attrName 属性名
499
+ * @example
500
+ * // 移除元素a.xx的属性data-value
501
+ * DOMUtils.removeAttr(document.querySelector("a.xx"),"data-value")
502
+ * DOMUtils.removeAttr("a.xx","data-value")
503
+ * */
504
+ removeAttr(element: HTMLElement | string, attrName: string) {
505
+ if (typeof element === "string") {
506
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
507
+ }
508
+ if (element == null) {
509
+ return;
510
+ }
511
+ element.removeAttribute(attrName);
512
+ }
513
+ /**
514
+ * 移除元素class名
515
+ * @param element 目标元素
516
+ * @param className 类名
517
+ * @example
518
+ * // 移除元素a.xx的className为xx
519
+ * DOMUtils.removeClass(document.querySelector("a.xx"),"xx")
520
+ * DOMUtils.removeClass("a.xx","xx")
521
+ */
522
+ removeClass(element: HTMLElement | string, className: string) {
523
+ if (typeof element === "string") {
524
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
525
+ }
526
+ if (element == null) {
527
+ return;
528
+ }
529
+ if (className == null) {
530
+ return;
531
+ }
532
+ element.classList.remove(className);
533
+ }
534
+ /**
535
+ * 移除元素的属性
536
+ * @param element 目标元素
537
+ * @param propName 属性名
538
+ * @example
539
+ * // 移除元素a.xx的href属性
540
+ * DOMUtils.removeProp(document.querySelector("a.xx"),"href")
541
+ * DOMUtils.removeProp("a.xx","href")
542
+ * */
543
+ removeProp(element: HTMLElement | string, propName: string) {
544
+ if (typeof element === "string") {
545
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
546
+ }
547
+ if (element == null) {
548
+ return;
549
+ }
550
+ DOMUtilsCommonUtils.delete(element, propName);
551
+ }
552
+ /**
553
+ * 将一个元素替换为另一个元素
554
+ * @param element 目标元素
555
+ * @param newElement 新元素
556
+ * @example
557
+ * // 替换元素a.xx为b.xx
558
+ * DOMUtils.replaceWith(document.querySelector("a.xx"),document.querySelector("b.xx"))
559
+ * DOMUtils.replaceWith("a.xx",'<b class="xx"></b>')
560
+ */
561
+ replaceWith(
562
+ element: HTMLElement | string | NodeList | HTMLElement[] | Node,
563
+ newElement: HTMLElement | string | Node
564
+ ) {
565
+ let DOMUtilsContext = this;
566
+ if (typeof element === "string") {
567
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
568
+ }
569
+ if (element == null) {
570
+ return;
571
+ }
572
+ if (typeof newElement === "string") {
573
+ newElement = DOMUtilsContext.parseHTML(newElement, false, false);
574
+ }
575
+ if (element instanceof NodeList || element instanceof Array) {
576
+ element.forEach((item) => {
577
+ DOMUtilsContext.replaceWith(item, newElement);
578
+ });
579
+ } else {
580
+ element!.parentElement!.replaceChild(newElement as Node, element);
581
+ }
582
+ }
583
+ /**
584
+ * 给元素添加class
585
+ * @param element 目标元素
586
+ * @param className class名
587
+ * @example
588
+ * // 元素a.xx的className添加_vue_
589
+ * DOMUtils.addClass(document.querySelector("a.xx"),"_vue_")
590
+ * DOMUtils.addClass("a.xx","_vue_")
591
+ * */
592
+ addClass(element: HTMLElement | string, className: string) {
593
+ if (typeof element === "string") {
594
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
595
+ }
596
+ if (element == null) {
597
+ return;
598
+ }
599
+ element.classList.add(className);
600
+ }
601
+ /**
602
+ * 函数在元素内部末尾添加子元素或HTML字符串
603
+ * @param element 目标元素
604
+ * @param content 子元素或HTML字符串
605
+ * @example
606
+ * // 元素a.xx的内部末尾添加一个元素
607
+ * DOMUtils.append(document.querySelector("a.xx"),document.querySelector("b.xx"))
608
+ * DOMUtils.append("a.xx","'<b class="xx"></b>")
609
+ * */
610
+ append(
611
+ element: HTMLElement | string,
612
+ content:
613
+ | HTMLElement
614
+ | string
615
+ | (HTMLElement | string | Element)[]
616
+ | NodeList
617
+ ) {
618
+ if (typeof element === "string") {
619
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
620
+ }
621
+ if (element == null) {
622
+ return;
623
+ }
624
+ function elementAppendChild(ele: HTMLElement, text: HTMLElement | string) {
625
+ if (typeof content === "string") {
626
+ ele.insertAdjacentHTML("beforeend", text as string);
627
+ } else {
628
+ ele.appendChild(text as HTMLElement);
629
+ }
630
+ }
631
+ if (Array.isArray(content) || content instanceof NodeList) {
632
+ /* 数组 */
633
+ let fragment = DOMUtilsCore.document.createDocumentFragment();
634
+ content.forEach((ele) => {
635
+ if (typeof ele === "string") {
636
+ ele = this.parseHTML(ele, true, false);
637
+ }
638
+ fragment.appendChild(ele);
639
+ });
640
+ element.appendChild(fragment);
641
+ } else {
642
+ elementAppendChild(element, content);
643
+ }
644
+ }
645
+ /**
646
+ * 函数 在元素内部开头添加子元素或HTML字符串
647
+ * @param element 目标元素
648
+ * @param content 子元素或HTML字符串
649
+ * @example
650
+ * // 元素a.xx内部开头添加一个元素
651
+ * DOMUtils.prepend(document.querySelector("a.xx"),document.querySelector("b.xx"))
652
+ * DOMUtils.prepend("a.xx","'<b class="xx"></b>")
653
+ * */
654
+ prepend(element: HTMLElement | string, content: HTMLElement | string) {
655
+ if (typeof element === "string") {
656
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
657
+ }
658
+ if (element == null) {
659
+ return;
660
+ }
661
+ if (typeof content === "string") {
662
+ element.insertAdjacentHTML("afterbegin", content);
663
+ } else {
664
+ element.insertBefore(content, element.firstChild);
665
+ }
666
+ }
667
+ /**
668
+ * 在元素后面添加兄弟元素或HTML字符串
669
+ * @param element 目标元素
670
+ * @param content 兄弟元素或HTML字符串
671
+ * @example
672
+ * // 元素a.xx后面添加一个元素
673
+ * DOMUtils.after(document.querySelector("a.xx"),document.querySelector("b.xx"))
674
+ * DOMUtils.after("a.xx","'<b class="xx"></b>")
675
+ * */
676
+ after(element: HTMLElement | string, content: HTMLElement | string) {
677
+ if (typeof element === "string") {
678
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
679
+ }
680
+ if (element == null) {
681
+ return;
682
+ }
683
+ if (typeof content === "string") {
684
+ element.insertAdjacentHTML("afterend", content);
685
+ } else {
686
+ element!.parentElement!.insertBefore(content, element.nextSibling);
687
+ }
688
+ }
689
+ /**
690
+ * 在元素前面添加兄弟元素或HTML字符串
691
+ * @param element 目标元素
692
+ * @param content 兄弟元素或HTML字符串
693
+ * @example
694
+ * // 元素a.xx前面添加一个元素
695
+ * DOMUtils.before(document.querySelector("a.xx"),document.querySelector("b.xx"))
696
+ * DOMUtils.before("a.xx","'<b class="xx"></b>")
697
+ * */
698
+ before(element: HTMLElement | string, content: HTMLElement | string) {
699
+ if (typeof element === "string") {
700
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
701
+ }
702
+ if (element == null) {
703
+ return;
704
+ }
705
+ if (typeof content === "string") {
706
+ element.insertAdjacentHTML("beforebegin", content);
707
+ } else {
708
+ element!.parentElement!.insertBefore(content, element);
709
+ }
710
+ }
711
+ /**
712
+ * 移除元素
713
+ * @param target 目标元素
714
+ * @example
715
+ * // 元素a.xx前面添加一个元素
716
+ * DOMUtils.remove(document.querySelector("a.xx"))
717
+ * DOMUtils.remove(document.querySelectorAll("a.xx"))
718
+ * DOMUtils.remove("a.xx")
719
+ * */
720
+ remove(target: HTMLElement | string | NodeList | HTMLElement[]) {
721
+ let DOMUtilsContext = this;
722
+ if (typeof target === "string") {
723
+ target = DOMUtilsCore.document.querySelectorAll(
724
+ target
725
+ ) as NodeListOf<HTMLElement>;
726
+ }
727
+ if (target == null) {
728
+ return;
729
+ }
730
+ if (target instanceof NodeList || target instanceof Array) {
731
+ target = target as HTMLElement[];
732
+ for (const element of target) {
733
+ DOMUtilsContext.remove(element);
734
+ }
735
+ } else {
736
+ target.remove();
737
+ }
738
+ }
739
+ /**
740
+ * 移除元素的所有子元素
741
+ * @param element 目标元素
742
+ * @example
743
+ * // 移除元素a.xx元素的所有子元素
744
+ * DOMUtils.empty(document.querySelector("a.xx"))
745
+ * DOMUtils.empty("a.xx")
746
+ * */
747
+ empty(element: HTMLElement | string) {
748
+ if (typeof element === "string") {
749
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
750
+ }
751
+ if (element == null) {
752
+ return;
753
+ }
754
+ element.innerHTML = "";
755
+ }
756
+ /**
757
+ * 获取元素相对于文档的偏移坐标(加上文档的滚动条)
758
+ * @param element 目标元素
759
+ * @example
760
+ * // 获取元素a.xx的对于文档的偏移坐标
761
+ * DOMUtils.offset(document.querySelector("a.xx"))
762
+ * DOMUtils.offset("a.xx")
763
+ * > 0
764
+ */
765
+ offset(element: HTMLElement | string) {
766
+ if (typeof element === "string") {
767
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
768
+ }
769
+ if (element == null) {
770
+ return;
771
+ }
772
+ let rect = element.getBoundingClientRect();
773
+ return {
774
+ /** y轴偏移 */
775
+ top: rect.top + DOMUtilsCore.globalThis.scrollY,
776
+ /** x轴偏移 */
777
+ left: rect.left + DOMUtilsCore.globalThis.scrollX,
778
+ };
779
+ }
780
+ /**
781
+ * 获取元素的宽度
782
+ * @param element 要获取宽度的元素
783
+ * @param isShow 是否已进行isShow,避免爆堆栈
784
+ * @returns 元素的宽度,单位为像素
785
+ * @example
786
+ * // 获取元素a.xx的宽度
787
+ * DOMUtils.width(document.querySelector("a.xx"))
788
+ * DOMUtils.width("a.xx")
789
+ * > 100
790
+ * // 获取window的宽度
791
+ * DOMUtils.width(window)
792
+ * > 400
793
+ * @example
794
+ * // 设置元素a.xx的宽度为200
795
+ * DOMUtils.width(document.querySelector("a.xx"),200)
796
+ * DOMUtils.width("a.xx",200)
797
+ */
798
+ width(
799
+ element: HTMLElement | string | Window | Document,
800
+ isShow?: boolean
801
+ ): number;
802
+ width(
803
+ element: HTMLElement | string | Window | Document,
804
+ isShow: boolean = false
805
+ ) {
806
+ let DOMUtilsContext = this;
807
+ if (typeof element === "string") {
808
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
809
+ }
810
+ if (element == null) {
811
+ return;
812
+ }
813
+ if (DOMUtilsCommonUtils.isWin(element)) {
814
+ return DOMUtilsCore.window.document.documentElement.clientWidth;
815
+ }
816
+ if ((element as HTMLElement).nodeType === 9) {
817
+ /* Document文档节点 */
818
+ element = element as Document;
819
+ return Math.max(
820
+ element.body.scrollWidth,
821
+ element.documentElement.scrollWidth,
822
+ element.body.offsetWidth,
823
+ element.documentElement.offsetWidth,
824
+ element.documentElement.clientWidth
825
+ );
826
+ }
827
+ if (
828
+ isShow ||
829
+ (!isShow && DOMUtilsCommonUtils.isShow(element as HTMLElement))
830
+ ) {
831
+ /* 已显示 */
832
+ /* 不从style中获取对应的宽度,因为可能使用了class定义了width !important */
833
+ element = element as HTMLElement;
834
+ /* 如果element.style.width为空 则从css里面获取是否定义了width信息如果定义了 则读取css里面定义的宽度width */
835
+ if (
836
+ parseFloat(
837
+ DOMUtilsCommonUtils.getStyleValue(element, "width").toString()
838
+ ) > 0
839
+ ) {
840
+ return parseFloat(
841
+ DOMUtilsCommonUtils.getStyleValue(element, "width").toString()
842
+ );
843
+ }
844
+
845
+ /* 如果从css里获取到的值不是大于0 可能是auto 则通过offsetWidth来进行计算 */
846
+ if (element.offsetWidth > 0) {
847
+ let borderLeftWidth = DOMUtilsCommonUtils.getStyleValue(
848
+ element,
849
+ "borderLeftWidth"
850
+ );
851
+ let borderRightWidth = DOMUtilsCommonUtils.getStyleValue(
852
+ element,
853
+ "borderRightWidth"
854
+ );
855
+ let paddingLeft = DOMUtilsCommonUtils.getStyleValue(
856
+ element,
857
+ "paddingLeft"
858
+ );
859
+ let paddingRight = DOMUtilsCommonUtils.getStyleValue(
860
+ element,
861
+ "paddingRight"
862
+ );
863
+ let backHeight =
864
+ parseFloat(element.offsetWidth.toString()) -
865
+ parseFloat(borderLeftWidth.toString()) -
866
+ parseFloat(borderRightWidth.toString()) -
867
+ parseFloat(paddingLeft.toString()) -
868
+ parseFloat(paddingRight.toString());
869
+ return parseFloat(backHeight.toString());
870
+ }
871
+ return 0;
872
+ } else {
873
+ /* 未显示 */
874
+ element = element as HTMLElement;
875
+ let { recovery } = DOMUtilsCommonUtils.showElement(element);
876
+ let width = DOMUtilsContext.width(element, true);
877
+ recovery();
878
+ return width;
879
+ }
880
+ }
881
+
882
+ /**
883
+ * 获取元素的高度
884
+ * @param element 要获取高度的元素
885
+ * @param isShow 是否已进行isShow,避免爆堆栈
886
+ * @returns 元素的高度,单位为像素
887
+ * @example
888
+ * // 获取元素a.xx的高度
889
+ * DOMUtils.height(document.querySelector("a.xx"))
890
+ * DOMUtils.height("a.xx")
891
+ * > 100
892
+ * // 获取window的高度
893
+ * DOMUtils.height(window)
894
+ * > 700
895
+ * @example
896
+ * // 设置元素a.xx的高度为200
897
+ * DOMUtils.height(document.querySelector("a.xx"),200)
898
+ * DOMUtils.height("a.xx",200)
899
+ */
900
+ height(
901
+ element: HTMLElement | string | Window | Document,
902
+ isShow?: boolean
903
+ ): number;
904
+ height(
905
+ element: HTMLElement | string | Window | Document,
906
+ isShow: boolean = false
907
+ ) {
908
+ let DOMUtilsContext = this;
909
+ if (DOMUtilsCommonUtils.isWin(element)) {
910
+ return DOMUtilsCore.window.document.documentElement.clientHeight;
911
+ }
912
+ if (typeof element === "string") {
913
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
914
+ }
915
+ if (element == null) {
916
+ // @ts-ignore
917
+ return;
918
+ }
919
+ if ((element as Document).nodeType === 9) {
920
+ element = element as Document;
921
+ /* Document文档节点 */
922
+ return Math.max(
923
+ element.body.scrollHeight,
924
+ element.documentElement.scrollHeight,
925
+ element.body.offsetHeight,
926
+ element.documentElement.offsetHeight,
927
+ element.documentElement.clientHeight
928
+ );
929
+ }
930
+ if (
931
+ isShow ||
932
+ (!isShow && DOMUtilsCommonUtils.isShow(element as HTMLElement))
933
+ ) {
934
+ element = element as HTMLElement;
935
+ /* 已显示 */
936
+ /* 从style中获取对应的高度,因为可能使用了class定义了width !important */
937
+ /* 如果element.style.height为空 则从css里面获取是否定义了height信息如果定义了 则读取css里面定义的高度height */
938
+ if (
939
+ parseFloat(
940
+ DOMUtilsCommonUtils.getStyleValue(element, "height").toString()
941
+ ) > 0
942
+ ) {
943
+ return parseFloat(
944
+ DOMUtilsCommonUtils.getStyleValue(element, "height").toString()
945
+ );
946
+ }
947
+
948
+ /* 如果从css里获取到的值不是大于0 可能是auto 则通过offsetHeight来进行计算 */
949
+ if (element.offsetHeight > 0) {
950
+ let borderTopWidth = DOMUtilsCommonUtils.getStyleValue(
951
+ element,
952
+ "borderTopWidth"
953
+ );
954
+ let borderBottomWidth = DOMUtilsCommonUtils.getStyleValue(
955
+ element,
956
+ "borderBottomWidth"
957
+ );
958
+ let paddingTop = DOMUtilsCommonUtils.getStyleValue(
959
+ element,
960
+ "paddingTop"
961
+ );
962
+ let paddingBottom = DOMUtilsCommonUtils.getStyleValue(
963
+ element,
964
+ "paddingBottom"
965
+ );
966
+ let backHeight =
967
+ parseFloat(element.offsetHeight.toString()) -
968
+ parseFloat(borderTopWidth.toString()) -
969
+ parseFloat(borderBottomWidth.toString()) -
970
+ parseFloat(paddingTop.toString()) -
971
+ parseFloat(paddingBottom.toString());
972
+ return parseFloat(backHeight.toString());
973
+ }
974
+ return 0;
975
+ } else {
976
+ /* 未显示 */
977
+ element = element as HTMLElement;
978
+ let { recovery } = DOMUtilsCommonUtils.showElement(element);
979
+ let height = DOMUtilsContext.height(element, true);
980
+ recovery();
981
+ return height;
982
+ }
983
+ }
984
+ /**
985
+ * 获取元素的外部宽度(包括边框和外边距)
986
+ * @param {HTMLElement|string} element 要获取外部宽度的元素
987
+ * @param {boolean} [isShow=false] 是否已进行isShow,避免爆堆栈
988
+ * @returns {number} 元素的外部宽度,单位为像素
989
+ * @example
990
+ * // 获取元素a.xx的外部宽度
991
+ * DOMUtils.outerWidth(document.querySelector("a.xx"))
992
+ * DOMUtils.outerWidth("a.xx")
993
+ * > 100
994
+ * // 获取window的外部宽度
995
+ * DOMUtils.outerWidth(window)
996
+ * > 400
997
+ */
998
+ outerWidth(
999
+ element: HTMLElement | string | Window | Document,
1000
+ isShow?: boolean
1001
+ ): number;
1002
+ outerWidth(
1003
+ element: HTMLElement | string | Window | Document,
1004
+ isShow: boolean = false
1005
+ ) {
1006
+ let DOMUtilsContext = this;
1007
+ if (DOMUtilsCommonUtils.isWin(element)) {
1008
+ return DOMUtilsCore.window.innerWidth;
1009
+ }
1010
+ if (typeof element === "string") {
1011
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
1012
+ }
1013
+ if (element == null) {
1014
+ // @ts-ignore
1015
+ return;
1016
+ }
1017
+ element = element as HTMLElement;
1018
+ if (isShow || (!isShow && DOMUtilsCommonUtils.isShow(element))) {
1019
+ let style = getComputedStyle(element, null);
1020
+ let marginLeft = DOMUtilsCommonUtils.getStyleValue(style, "marginLeft");
1021
+ let marginRight = DOMUtilsCommonUtils.getStyleValue(style, "marginRight");
1022
+ return element.offsetWidth + marginLeft + marginRight;
1023
+ } else {
1024
+ let { recovery } = DOMUtilsCommonUtils.showElement(element);
1025
+ let outerWidth = DOMUtilsContext.outerWidth(element, true);
1026
+ recovery();
1027
+ return outerWidth;
1028
+ }
1029
+ }
1030
+ /**
1031
+ * 获取元素的外部高度(包括边框和外边距)
1032
+ * @param {HTMLElement|string} element 要获取外部高度的元素
1033
+ * @param {boolean} [isShow=false] 是否已进行isShow,避免爆堆栈
1034
+ * @returns {number} 元素的外部高度,单位为像素
1035
+ * @example
1036
+ * // 获取元素a.xx的外部高度
1037
+ * DOMUtils.outerHeight(document.querySelector("a.xx"))
1038
+ * DOMUtils.outerHeight("a.xx")
1039
+ * > 100
1040
+ * // 获取window的外部高度
1041
+ * DOMUtils.outerHeight(window)
1042
+ * > 700
1043
+ */
1044
+ outerHeight(element: HTMLElement | string | Window, isShow?: boolean): number;
1045
+ outerHeight(
1046
+ element: HTMLElement | string | Window,
1047
+ isShow: boolean = false
1048
+ ): number {
1049
+ let DOMUtilsContext = this;
1050
+ if (DOMUtilsCommonUtils.isWin(element)) {
1051
+ return DOMUtilsCore.window.innerHeight;
1052
+ }
1053
+ if (typeof element === "string") {
1054
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
1055
+ }
1056
+ if (element == null) {
1057
+ // @ts-ignore
1058
+ return;
1059
+ }
1060
+ element = element as HTMLElement;
1061
+ if (isShow || (!isShow && DOMUtilsCommonUtils.isShow(element))) {
1062
+ let style = getComputedStyle(element, null);
1063
+ let marginTop = DOMUtilsCommonUtils.getStyleValue(style, "marginTop");
1064
+ let marginBottom = DOMUtilsCommonUtils.getStyleValue(
1065
+ style,
1066
+ "marginBottom"
1067
+ );
1068
+ return element.offsetHeight + marginTop + marginBottom;
1069
+ } else {
1070
+ let { recovery } = DOMUtilsCommonUtils.showElement(element);
1071
+ let outerHeight = DOMUtilsContext.outerHeight(element, true);
1072
+ recovery();
1073
+ return outerHeight;
1074
+ }
1075
+ }
1076
+ /**
1077
+ * 在一定时间内改变元素的样式属性,实现动画效果
1078
+ * @param element 需要进行动画的元素
1079
+ * @param styles 动画结束时元素的样式属性
1080
+ * @param duration 动画持续时间,单位为毫秒
1081
+ * @param callback 动画结束后执行的函数
1082
+ * @example
1083
+ * // 监听元素a.xx的从显示变为隐藏
1084
+ * DOMUtils.animate(document.querySelector("a.xx"),{ top:100},1000,function(){
1085
+ * console.log("已往上位移100px")
1086
+ * })
1087
+ */
1088
+ animate(
1089
+ element: HTMLElement | string,
1090
+ styles: CSSStyleDeclaration,
1091
+ duration: number = 1000,
1092
+ callback: (() => void) | undefined | null = null
1093
+ ) {
1094
+ if (typeof element === "string") {
1095
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
1096
+ }
1097
+ if (element == null) {
1098
+ return;
1099
+ }
1100
+ if (typeof duration !== "number" || duration <= 0) {
1101
+ throw new TypeError("duration must be a positive number");
1102
+ }
1103
+ if (typeof callback !== "function" && callback !== void 0) {
1104
+ throw new TypeError("callback must be a function or null");
1105
+ }
1106
+ if (typeof styles !== "object" || styles === void 0) {
1107
+ throw new TypeError("styles must be an object");
1108
+ }
1109
+ if (Object.keys(styles).length === 0) {
1110
+ throw new Error("styles must contain at least one property");
1111
+ }
1112
+ let start = performance.now();
1113
+ let from: {
1114
+ [prop: string]: any;
1115
+ } = {};
1116
+ let to: {
1117
+ [prop: string]: any;
1118
+ } = {};
1119
+ for (let prop in styles) {
1120
+ from[prop] = element.style[prop] || getComputedStyle(element)[prop];
1121
+ to[prop] = styles[prop];
1122
+ }
1123
+ let timer = setInterval(function () {
1124
+ let timePassed = performance.now() - start;
1125
+ let progress = timePassed / duration;
1126
+ if (progress > 1) {
1127
+ progress = 1;
1128
+ }
1129
+ for (let prop in styles) {
1130
+ element.style[prop] =
1131
+ from[prop] + (to[prop] - from[prop]) * progress + "px";
1132
+ }
1133
+ if (progress === 1) {
1134
+ clearInterval(timer);
1135
+ if (callback) {
1136
+ callback();
1137
+ }
1138
+ }
1139
+ }, 10);
1140
+ }
1141
+ /**
1142
+ * 将一个元素包裹在指定的HTML元素中
1143
+ * @param element 要包裹的元素
1144
+ * @param wrapperHTML 要包裹的HTML元素的字符串表示形式
1145
+ * @example
1146
+ * // 将a.xx元素外面包裹一层div
1147
+ * DOMUtils.wrap(document.querySelector("a.xx"),"<div></div>")
1148
+ */
1149
+ wrap(element: HTMLElement | string | Node, wrapperHTML: string) {
1150
+ if (typeof element === "string") {
1151
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
1152
+ }
1153
+ if (element == null) {
1154
+ return;
1155
+ }
1156
+ element = element as HTMLElement;
1157
+ // 创建一个新的div元素,并将wrapperHTML作为其innerHTML
1158
+ let wrapper = DOMUtilsCore.document.createElement("div");
1159
+ wrapper.innerHTML = wrapperHTML;
1160
+
1161
+ let wrapperFirstChild = wrapper.firstChild as HTMLElement;
1162
+ // 将要包裹的元素插入目标元素前面
1163
+ (element.parentElement as HTMLElement).insertBefore(
1164
+ wrapperFirstChild,
1165
+ element
1166
+ );
1167
+
1168
+ // 将要包裹的元素移动到wrapper中
1169
+ wrapperFirstChild.appendChild(element);
1170
+ }
1171
+ /**
1172
+ * 获取当前元素的前一个兄弟元素
1173
+ * @param element 当前元素
1174
+ * @returns 前一个兄弟元素
1175
+ * @example
1176
+ * // 获取a.xx元素前一个兄弟元素
1177
+ * DOMUtils.prev(document.querySelector("a.xx"))
1178
+ * DOMUtils.prev("a.xx")
1179
+ * > <div ...>....</div>
1180
+ */
1181
+ prev(element: HTMLElement | string): HTMLElement;
1182
+ prev(element: HTMLElement | string) {
1183
+ if (typeof element === "string") {
1184
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
1185
+ }
1186
+ if (element == null) {
1187
+ return;
1188
+ }
1189
+ return element.previousElementSibling as HTMLElement;
1190
+ }
1191
+
1192
+ /**
1193
+ * 获取当前元素的后一个兄弟元素
1194
+ * @param element 当前元素
1195
+ * @returns 后一个兄弟元素
1196
+ * @example
1197
+ * // 获取a.xx元素前一个兄弟元素
1198
+ * DOMUtils.next(document.querySelector("a.xx"))
1199
+ * DOMUtils.next("a.xx")
1200
+ * > <div ...>....</div>
1201
+ */
1202
+ next(element: HTMLElement | string): HTMLElement;
1203
+ next(element: HTMLElement | string) {
1204
+ if (typeof element === "string") {
1205
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
1206
+ }
1207
+ if (element == null) {
1208
+ return;
1209
+ }
1210
+ return element.nextElementSibling as HTMLElement;
1211
+ }
1212
+ /**
1213
+ * 取消挂载在window下的DOMUtils并返回DOMUtils
1214
+ * @example
1215
+ * let DOMUtils = window.DOMUtils.noConflict()
1216
+ */
1217
+ noConflict() {
1218
+ if ((DOMUtilsCore.window as any).DOMUtils) {
1219
+ DOMUtilsCommonUtils.delete(window, "DOMUtils");
1220
+ }
1221
+ (DOMUtilsCore.window as any).DOMUtils = this;
1222
+ return this;
1223
+ }
1224
+ /**
1225
+ * 获取当前元素的所有兄弟元素
1226
+ * @param element 当前元素
1227
+ * @returns 所有兄弟元素
1228
+ * @example
1229
+ * // 获取a.xx元素所有兄弟元素
1230
+ * DOMUtils.siblings(document.querySelector("a.xx"))
1231
+ * DOMUtils.siblings("a.xx")
1232
+ * > (3) [div.logo-wrapper, div.forum-block, div.more-btn-desc]
1233
+ */
1234
+ siblings(element: HTMLElement | string): HTMLElement[];
1235
+ siblings(element: HTMLElement | string) {
1236
+ if (typeof element === "string") {
1237
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
1238
+ }
1239
+ if (element == null) {
1240
+ return;
1241
+ }
1242
+ return Array.from(
1243
+ (element.parentElement as HTMLElement)
1244
+ .children as HTMLCollectionOf<HTMLElement>
1245
+ ).filter((child) => child !== element);
1246
+ }
1247
+ /**
1248
+ * 获取当前元素的父元素
1249
+ * @param element 当前元素
1250
+ * @returns 父元素
1251
+ * @example
1252
+ * // 获取a.xx元素的父元素
1253
+ * DOMUtils.parent(document.querySelector("a.xx"))
1254
+ * DOMUtils.parent("a.xx")
1255
+ * > <div ...>....</div>
1256
+ */
1257
+ parent(element: HTMLElement | string): HTMLElement;
1258
+ /**
1259
+ * 获取当前元素的父元素
1260
+ * @param element 当前元素
1261
+ * @returns 父元素
1262
+ * @example
1263
+ * // 获取a.xx元素的父元素
1264
+ * DOMUtils.parent(document.querySelector("a.xx"))
1265
+ * DOMUtils.parent("a.xx")
1266
+ * > <div ...>....</div>
1267
+ */
1268
+ parent(element: HTMLElement[] | NodeList): HTMLElement[];
1269
+ /**
1270
+ * 获取当前元素的父元素
1271
+ * @param element 当前元素
1272
+ * @returns 父元素
1273
+ * @example
1274
+ * // 获取a.xx元素的父元素
1275
+ * DOMUtils.parent(document.querySelector("a.xx"))
1276
+ * DOMUtils.parent("a.xx")
1277
+ * > <div ...>....</div>
1278
+ */
1279
+ parent(element: HTMLElement | NodeList | string | HTMLElement[]) {
1280
+ let DOMUtilsContext = this;
1281
+ if (typeof element === "string") {
1282
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
1283
+ }
1284
+ if (element == null) {
1285
+ return;
1286
+ }
1287
+ if (element instanceof NodeList || element instanceof Array) {
1288
+ element = element as HTMLElement[];
1289
+ let resultArray: HTMLElement[] = [];
1290
+ element.forEach((eleItem) => {
1291
+ resultArray.push(DOMUtilsContext.parent(eleItem));
1292
+ });
1293
+ return resultArray;
1294
+ } else {
1295
+ return element.parentElement;
1296
+ }
1297
+ }
1298
+ /**
1299
+ * 将字符串转为Element元素
1300
+ * @param html
1301
+ * @param useParser 是否使用DOMParser来生成元素,有些时候通过DOMParser生成的元素有点问题
1302
+ * + true 使用DOMPraser来转换字符串
1303
+ * + false (默认)创建一个div,里面放入字符串,然后提取firstChild
1304
+ * @param isComplete 是否是完整的
1305
+ * + true 如果useParser为true,那么返回整个使用DOMParser转换成的Document
1306
+ * 如果useParser为false,返回一个DIV元素,DIV元素内包裹着需要转换的字符串
1307
+ * + false (默认)如果useParser为true,那么返回整个使用DOMParser转换成的Document的body
1308
+ * 如果useParser为false,返回一个DIV元素的firstChild
1309
+ * @example
1310
+ * // 将字符串转为Element元素
1311
+ * DOMUtils.parseHTML("<a href='xxxx'></a>")
1312
+ * > <a href="xxxx"></a>
1313
+ * @example
1314
+ * // 使用DOMParser将字符串转为Element元素
1315
+ * DOMUtils.parseHTML("<a href='xxxx'></a>",true)
1316
+ * > <a href="xxxx"></a>
1317
+ * @example
1318
+ * // 由于需要转换的元素是多个元素,将字符串转为完整的Element元素
1319
+ * DOMUtils.parseHTML("<a href='xxxx'></a><a href='xxxx'></a>",false, true)
1320
+ * > <div><a href="xxxx"></a><a href='xxxx'></a></div>
1321
+ * @example
1322
+ * // 由于需要转换的元素是多个元素,使用DOMParser将字符串转为完整的Element元素
1323
+ * DOMUtils.parseHTML("<a href='xxxx'></a><a href='xxxx'></a>",true, true)
1324
+ * > #document
1325
+ */
1326
+ parseHTML<T1 extends boolean, T2 extends boolean>(
1327
+ html: string,
1328
+ useParser?: T1,
1329
+ isComplete?: T2
1330
+ ): ParseHTMLReturnType<T1, T2>;
1331
+ parseHTML(html: string, useParser = false, isComplete = false) {
1332
+ function parseHTMLByDOMParser() {
1333
+ let parser = new DOMParser();
1334
+ if (isComplete) {
1335
+ return parser.parseFromString(html, "text/html");
1336
+ } else {
1337
+ return parser.parseFromString(html, "text/html").body.firstChild;
1338
+ }
1339
+ }
1340
+ function parseHTMLByCreateDom() {
1341
+ let tempDIV = DOMUtilsCore.document.createElement("div");
1342
+ tempDIV.innerHTML = html;
1343
+ if (isComplete) {
1344
+ return tempDIV;
1345
+ } else {
1346
+ return tempDIV.firstChild;
1347
+ }
1348
+ }
1349
+ if (useParser) {
1350
+ return parseHTMLByDOMParser();
1351
+ } else {
1352
+ return parseHTMLByCreateDom();
1353
+ }
1354
+ }
1355
+ /**
1356
+ * 显示元素
1357
+ * @param target 当前元素
1358
+ * @example
1359
+ * // 显示a.xx元素
1360
+ * DOMUtils.show(document.querySelector("a.xx"))
1361
+ * DOMUtils.show(document.querySelectorAll("a.xx"))
1362
+ * DOMUtils.show("a.xx")
1363
+ */
1364
+ show(target: HTMLElement | string | NodeList | HTMLElement[]) {
1365
+ let DOMUtilsContext = this;
1366
+ if (target == null) {
1367
+ return;
1368
+ }
1369
+ if (typeof target === "string") {
1370
+ target = DOMUtilsCore.document.querySelectorAll(target);
1371
+ }
1372
+ if (target instanceof NodeList || target instanceof Array) {
1373
+ target = target as HTMLElement[];
1374
+ for (const element of target) {
1375
+ DOMUtilsContext.show(element);
1376
+ }
1377
+ } else {
1378
+ target = target as HTMLElement;
1379
+ target.style.display = "";
1380
+ if (!DOMUtilsCommonUtils.isShow(target)) {
1381
+ /* 仍然是不显示,尝试使用强覆盖 */
1382
+ target.style.setProperty("display", "unset", "important");
1383
+ }
1384
+ }
1385
+ }
1386
+ /**
1387
+ * 隐藏元素
1388
+ * @param target 当前元素
1389
+ * @example
1390
+ * // 隐藏a.xx元素
1391
+ * DOMUtils.hide(document.querySelector("a.xx"))
1392
+ * DOMUtils.hide(document.querySelectorAll("a.xx"))
1393
+ * DOMUtils.hide("a.xx")
1394
+ */
1395
+ hide(target: HTMLElement | string | NodeList | HTMLElement[]) {
1396
+ let DOMUtilsContext = this;
1397
+ if (target == null) {
1398
+ return;
1399
+ }
1400
+ if (typeof target === "string") {
1401
+ target = DOMUtilsCore.document.querySelectorAll(target);
1402
+ }
1403
+ if (target instanceof NodeList || target instanceof Array) {
1404
+ target = target as HTMLElement[];
1405
+ for (const element of target) {
1406
+ DOMUtilsContext.hide(element);
1407
+ }
1408
+ } else {
1409
+ target = target as HTMLElement;
1410
+ target.style.display = "none";
1411
+ if (DOMUtilsCommonUtils.isShow(target)) {
1412
+ /* 仍然是显示,尝试使用强覆盖 */
1413
+ target.style.setProperty("display", "none", "important");
1414
+ }
1415
+ }
1416
+ }
1417
+ /**
1418
+ * 淡入元素
1419
+ * @param element 当前元素
1420
+ * @param duration 动画持续时间(毫秒),默认400毫秒
1421
+ * @param callback 动画结束的回调
1422
+ * @example
1423
+ * // 元素a.xx淡入
1424
+ * DOMUtils.fadeIn(document.querySelector("a.xx"),2500,()=>{
1425
+ * console.log("淡入完毕");
1426
+ * })
1427
+ * DOMUtils.fadeIn("a.xx",undefined,()=>{
1428
+ * console.log("淡入完毕");
1429
+ * })
1430
+ */
1431
+ fadeIn(
1432
+ element: HTMLElement | string,
1433
+ duration: number = 400,
1434
+ callback?: () => void
1435
+ ) {
1436
+ if (element == null) {
1437
+ return;
1438
+ }
1439
+ if (typeof element === "string") {
1440
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
1441
+ }
1442
+ element = element as HTMLElement;
1443
+ element.style.opacity = "0";
1444
+ element.style.display = "";
1445
+ let start: number = null as any;
1446
+ let timer: number = null as any;
1447
+ function step(timestamp: number) {
1448
+ if (!start) start = timestamp;
1449
+ let progress = timestamp - start;
1450
+ element = element as HTMLElement;
1451
+ element.style.opacity = Math.min(progress / duration, 1).toString();
1452
+ if (progress < duration) {
1453
+ DOMUtilsCore.window.requestAnimationFrame(step);
1454
+ } else {
1455
+ if (callback && typeof callback === "function") {
1456
+ callback();
1457
+ }
1458
+ DOMUtilsCore.window.cancelAnimationFrame(timer);
1459
+ }
1460
+ }
1461
+ timer = DOMUtilsCore.window.requestAnimationFrame(step);
1462
+ }
1463
+ /**
1464
+ * 淡出元素
1465
+ * @param element 当前元素
1466
+ * @param duration 动画持续时间(毫秒),默认400毫秒
1467
+ * @param callback 动画结束的回调
1468
+ * @example
1469
+ * // 元素a.xx淡出
1470
+ * DOMUtils.fadeOut(document.querySelector("a.xx"),2500,()=>{
1471
+ * console.log("淡出完毕");
1472
+ * })
1473
+ * DOMUtils.fadeOut("a.xx",undefined,()=>{
1474
+ * console.log("淡出完毕");
1475
+ * })
1476
+ */
1477
+ fadeOut(
1478
+ element: HTMLElement | string,
1479
+ duration: number = 400,
1480
+ callback?: () => void
1481
+ ) {
1482
+ if (element == null) {
1483
+ return;
1484
+ }
1485
+ if (typeof element === "string") {
1486
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
1487
+ }
1488
+ element = element as HTMLElement;
1489
+ element.style.opacity = "1";
1490
+ let start: number = null as any;
1491
+ let timer: number = null as any;
1492
+ function step(timestamp: number) {
1493
+ if (!start) start = timestamp;
1494
+ let progress = timestamp - start;
1495
+ element = element as HTMLElement;
1496
+ element.style.opacity = Math.max(1 - progress / duration, 0).toString();
1497
+ if (progress < duration) {
1498
+ DOMUtilsCore.window.requestAnimationFrame(step);
1499
+ } else {
1500
+ element.style.display = "none";
1501
+ if (typeof callback === "function") {
1502
+ callback();
1503
+ }
1504
+ DOMUtilsCore.window.cancelAnimationFrame(timer);
1505
+ }
1506
+ }
1507
+ timer = DOMUtilsCore.window.requestAnimationFrame(step);
1508
+ }
1509
+ /**
1510
+ * 切换元素的显示和隐藏状态
1511
+ * @param element 当前元素
1512
+ * @example
1513
+ * // 如果元素a.xx当前是隐藏,则显示,如果是显示,则隐藏
1514
+ * DOMUtils.toggle(document.querySelector("a.xx"))
1515
+ * DOMUtils.toggle("a.xx")
1516
+ */
1517
+ toggle(element: HTMLElement | string) {
1518
+ let DOMUtilsContext = this;
1519
+ if (typeof element === "string") {
1520
+ element = DOMUtilsCore.document.querySelector(element) as HTMLElement;
1521
+ }
1522
+ if (element == null) {
1523
+ return;
1524
+ }
1525
+ if (getComputedStyle(element).getPropertyValue("display") === "none") {
1526
+ DOMUtilsContext.show(element);
1527
+ } else {
1528
+ DOMUtilsContext.hide(element);
1529
+ }
1530
+ }
1531
+ /**
1532
+ * 创建一个新的DOMUtils实例
1533
+ * @param option
1534
+ * @returns
1535
+ */
1536
+ createDOMUtils(option?: DOMUtilsCoreOption) {
1537
+ return new DOMUtils(option);
1538
+ }
1539
+ /**
1540
+ * 获取文字的位置信息
1541
+ * @param $input 输入框
1542
+ * @param selectionStart 起始位置
1543
+ * @param selectionEnd 结束位置
1544
+ * @example
1545
+ * DOMUtils.getTextBoundingRect(document.querySelector("input"));
1546
+ */
1547
+ getTextBoundingRect(
1548
+ $input: HTMLInputElement,
1549
+ selectionStart?: number | string,
1550
+ selectionEnd?: number | string
1551
+ ): DOMRect {
1552
+ // Basic parameter validation
1553
+ if (!$input || !("value" in $input)) return $input;
1554
+ if (selectionStart == null) {
1555
+ selectionStart = $input.selectionStart || 0;
1556
+ }
1557
+ if (selectionEnd == null) {
1558
+ selectionEnd = $input.selectionEnd || 0;
1559
+ }
1560
+ if (typeof selectionStart == "string")
1561
+ selectionStart = parseFloat(selectionStart);
1562
+ if (typeof selectionStart != "number" || isNaN(selectionStart)) {
1563
+ selectionStart = 0;
1564
+ }
1565
+ if (selectionStart < 0) selectionStart = 0;
1566
+ else selectionStart = Math.min($input.value.length, selectionStart);
1567
+ if (typeof selectionEnd == "string")
1568
+ selectionEnd = parseFloat(selectionEnd);
1569
+ if (
1570
+ typeof selectionEnd != "number" ||
1571
+ isNaN(selectionEnd) ||
1572
+ selectionEnd < selectionStart
1573
+ ) {
1574
+ selectionEnd = selectionStart;
1575
+ }
1576
+ if (selectionEnd < 0) selectionEnd = 0;
1577
+ else selectionEnd = Math.min($input.value.length, selectionEnd);
1578
+
1579
+ // If available (thus IE), use the createTextRange method
1580
+ // @ts-ignore
1581
+ if (typeof $input.createTextRange == "function") {
1582
+ let range = ($input as any).createTextRange();
1583
+ range.collapse(true);
1584
+ range.moveStart("character", selectionStart);
1585
+ range.moveEnd("character", selectionEnd - selectionStart);
1586
+ return range.getBoundingClientRect();
1587
+ }
1588
+ // createTextRange is not supported, create a fake text range
1589
+ let offset = getInputOffset(),
1590
+ topPos = offset.top,
1591
+ leftPos = offset.left,
1592
+ width = getInputCSS("width", true),
1593
+ height = getInputCSS("height", true);
1594
+
1595
+ // Styles to simulate a node in an input field
1596
+ let cssDefaultStyles = "white-space:pre;padding:0;margin:0;",
1597
+ listOfModifiers = [
1598
+ "direction",
1599
+ "font-family",
1600
+ "font-size",
1601
+ "font-size-adjust",
1602
+ "font-variant",
1603
+ "font-weight",
1604
+ "font-style",
1605
+ "letter-spacing",
1606
+ "line-height",
1607
+ "text-align",
1608
+ "text-indent",
1609
+ "text-transform",
1610
+ "word-wrap",
1611
+ "word-spacing",
1612
+ ];
1613
+ topPos += getInputCSS("padding-top", true) as number;
1614
+ topPos += getInputCSS("border-top-width", true) as number;
1615
+ leftPos += getInputCSS("padding-left", true) as number;
1616
+ leftPos += getInputCSS("border-left-width", true) as number;
1617
+ leftPos += 1; //Seems to be necessary
1618
+
1619
+ for (let index = 0; index < listOfModifiers.length; index++) {
1620
+ let property = listOfModifiers[index];
1621
+ // @ts-ignore
1622
+ cssDefaultStyles += property + ":" + getInputCSS(property) + ";";
1623
+ }
1624
+ // End of CSS variable checks
1625
+ // 不能为空,不然获取不到高度
1626
+ let text = $input.value || "G",
1627
+ textLen = text.length,
1628
+ fakeClone = DOMUtilsCore.document.createElement("div");
1629
+ if (selectionStart > 0) appendPart(0, selectionStart);
1630
+ var fakeRange = appendPart(selectionStart, selectionEnd);
1631
+ if (textLen > selectionEnd) appendPart(selectionEnd, textLen);
1632
+
1633
+ // Styles to inherit the font styles of the element
1634
+ fakeClone.style.cssText = cssDefaultStyles;
1635
+
1636
+ // Styles to position the text node at the desired position
1637
+ fakeClone.style.position = "absolute";
1638
+ fakeClone.style.top = topPos + "px";
1639
+ fakeClone.style.left = leftPos + "px";
1640
+ fakeClone.style.width = width + "px";
1641
+ fakeClone.style.height = height + "px";
1642
+ DOMUtilsCore.document.body.appendChild(fakeClone);
1643
+ var returnValue = fakeRange.getBoundingClientRect(); //Get rect
1644
+
1645
+ fakeClone?.parentNode?.removeChild(fakeClone); //Remove temp
1646
+ return returnValue;
1647
+
1648
+ // Local functions for readability of the previous code
1649
+ /**
1650
+ *
1651
+ * @param start
1652
+ * @param end
1653
+ * @returns
1654
+ */
1655
+ function appendPart(start: number, end: number) {
1656
+ var span = DOMUtilsCore.document.createElement("span");
1657
+ span.style.cssText = cssDefaultStyles; //Force styles to prevent unexpected results
1658
+ span.textContent = text.substring(start, end);
1659
+ fakeClone.appendChild(span);
1660
+ return span;
1661
+ }
1662
+ // Computing offset position
1663
+ function getInputOffset() {
1664
+ let body = DOMUtilsCore.document.body,
1665
+ win = DOMUtilsCore.document.defaultView!,
1666
+ docElem = DOMUtilsCore.document.documentElement,
1667
+ $box = DOMUtilsCore.document.createElement("div");
1668
+ $box.style.paddingLeft = $box.style.width = "1px";
1669
+ body.appendChild($box);
1670
+ var isBoxModel = $box.offsetWidth == 2;
1671
+ body.removeChild($box);
1672
+ let $boxRect = $input.getBoundingClientRect();
1673
+ var clientTop = docElem.clientTop || body.clientTop || 0,
1674
+ clientLeft = docElem.clientLeft || body.clientLeft || 0,
1675
+ scrollTop =
1676
+ win.pageYOffset ||
1677
+ (isBoxModel && docElem.scrollTop) ||
1678
+ body.scrollTop,
1679
+ scrollLeft =
1680
+ win.pageXOffset ||
1681
+ (isBoxModel && docElem.scrollLeft) ||
1682
+ body.scrollLeft;
1683
+ return {
1684
+ top: $boxRect.top + scrollTop - clientTop,
1685
+ left: $boxRect.left + scrollLeft - clientLeft,
1686
+ };
1687
+ }
1688
+ /**
1689
+ *
1690
+ * @param prop
1691
+ * @param isNumber
1692
+ * @returns
1693
+ */
1694
+ function getInputCSS(prop: string, isNumber: boolean) {
1695
+ var val = DOMUtilsCore.document
1696
+ .defaultView!.getComputedStyle($input, null)
1697
+ .getPropertyValue(prop);
1698
+ return isNumber ? parseFloat(val) : val;
1699
+ }
1700
+ }
1701
+ }
1702
+
1703
+ let domUtils = new DOMUtils();
1704
+
1705
+ export { domUtils as DOMUtils };