@whitesev/domutils 1.9.9 → 1.9.11

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.
@@ -369,6 +369,7 @@ export declare type DOMUtilsEventListenerOption = AddEventListenerOptions & {
369
369
  * 是否使用 event.composedPath() 来代替 event.target
370
370
  *
371
371
  * 一般用于设置了selector参数
372
+ * @default false
372
373
  */
373
374
  isComposedPath?: boolean;
374
375
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@whitesev/domutils",
4
- "version": "1.9.9",
4
+ "version": "1.9.11",
5
5
  "description": "适合在浏览器中操作DOM的常用工具类",
6
6
  "keywords": [
7
7
  "typescript",
@@ -49,7 +49,7 @@ class ElementEvent extends ElementAnimate {
49
49
  * console.log("事件触发",event)
50
50
  * })
51
51
  */
52
- on<T extends DOMUtils_EventType>(
52
+ on<T extends DOMUtils_EventType = DOMUtils_EventType>(
53
53
  element: DOMUtilsElementEventType,
54
54
  eventType: T | T[],
55
55
  handler: <E extends HTMLElement = HTMLElement>(this: E, event: DOMUtils_Event[T]) => void,
@@ -73,7 +73,7 @@ class ElementEvent extends ElementAnimate {
73
73
  * console.log("事件触发",event)
74
74
  * })
75
75
  */
76
- on<T extends Event>(
76
+ on<T extends Event = Event>(
77
77
  element: DOMUtilsElementEventType,
78
78
  eventType: string | string[],
79
79
  handler: <E extends HTMLElement = HTMLElement>(this: E, event: T) => void,
@@ -103,7 +103,7 @@ class ElementEvent extends ElementAnimate {
103
103
  * console.log("事件触发", event, $selector)
104
104
  * })
105
105
  */
106
- on<T extends DOMUtils_EventType>(
106
+ on<T extends DOMUtils_EventType = DOMUtils_EventType>(
107
107
  element: DOMUtilsElementEventType,
108
108
  eventType: T | T[],
109
109
  selector: string | string[] | undefined | null,
@@ -134,14 +134,14 @@ class ElementEvent extends ElementAnimate {
134
134
  * console.log("事件触发", event, $selector)
135
135
  * })
136
136
  */
137
- on<T extends Event>(
137
+ on<T extends Event = Event>(
138
138
  element: DOMUtilsElementEventType,
139
139
  eventType: string | string[],
140
140
  selector: string | string[] | undefined | null,
141
141
  handler: <E extends HTMLElement = HTMLElement>(this: E, event: T, $selector: E) => void,
142
142
  option?: DOMUtilsEventListenerOption | boolean
143
143
  ): DOMUtilsAddEventListenerResult;
144
- on<T extends Event>(
144
+ on<T extends Event = Event>(
145
145
  element: HTMLElement | string | NodeList | HTMLElement[] | Window | Document | Element | null | typeof globalThis,
146
146
  eventType: DOMUtils_EventType | DOMUtils_EventType[] | string | string[],
147
147
  selector:
@@ -162,7 +162,7 @@ class ElementEvent extends ElementAnimate {
162
162
  * @param startIndex
163
163
  * @param option
164
164
  */
165
- function getOption(args: IArguments, startIndex: number, option: DOMUtilsEventListenerOption) {
165
+ const getOption = function (args: IArguments, startIndex: number, option: DOMUtilsEventListenerOption) {
166
166
  const currentParam = args[startIndex];
167
167
  if (typeof currentParam === "boolean") {
168
168
  option.capture = currentParam;
@@ -185,7 +185,7 @@ class ElementEvent extends ElementAnimate {
185
185
  option.isComposedPath = currentParam.isComposedPath;
186
186
  }
187
187
  return option;
188
- }
188
+ };
189
189
 
190
190
  const that = this;
191
191
  // eslint-disable-next-line prefer-rest-params
@@ -220,7 +220,11 @@ class ElementEvent extends ElementAnimate {
220
220
  selectorList.push(selector);
221
221
  }
222
222
  // 事件回调
223
- let listenerCallBack: (this: HTMLElement, event: Event, $selector?: HTMLElement) => void = callback as any;
223
+ let listenerCallBack: (this: Element, event: Event, $selector?: HTMLElement) => void | boolean = callback as (
224
+ this: Element,
225
+ event: Event,
226
+ $selector?: HTMLElement
227
+ ) => void | boolean;
224
228
  // 事件配置
225
229
  let listenerOption: DOMUtilsEventListenerOption = {
226
230
  capture: false,
@@ -237,75 +241,95 @@ class ElementEvent extends ElementAnimate {
237
241
  // 这是存在selector的情况
238
242
  listenerOption = getOption(args, 4, listenerOption);
239
243
  }
240
- /**
241
- * 如果是once,那么删除该监听和元素上的事件和监听
242
- */
243
- const checkOptionOnceToRemoveEventListener = () => {
244
- if (listenerOption.once) {
245
- that.off(element, eventType as any, selector as any, callback as any, option);
246
- }
247
- };
248
- $elList.forEach((elementItem) => {
249
- /**
250
- * 事件回调
251
- * @param event
252
- */
253
- const handlerCallBack = function (event: Event) {
254
- if (selectorList.length) {
255
- /* 存在子元素选择器 */
256
- // 这时候的this和target都是子元素选择器的元素
257
- let eventTarget = listenerOption.isComposedPath
258
- ? (event.composedPath()[0] as HTMLElement)
259
- : (event.target as HTMLElement);
260
- let totalParent = elementItem;
261
- if (CommonUtils.isWin(totalParent)) {
262
- if (totalParent === (that.windowApi.document as any as HTMLElement)) {
263
- totalParent = that.windowApi.document.documentElement;
264
- }
244
+ $elList.forEach(($elItem) => {
245
+ // 遍历事件名设置元素事件
246
+ eventTypeList.forEach((eventName) => {
247
+ /**
248
+ * 如果是option.once,那么删除该监听和元素上的事件和监听
249
+ */
250
+ const checkOptionOnceToRemoveEventListener = () => {
251
+ if (listenerOption.once) {
252
+ this.off($elItem, eventName, selector as any, callback as any, option);
265
253
  }
266
- const findValue = selectorList.find((selectorItem) => {
267
- // 判断目标元素是否匹配选择器
268
- if (that.matches(eventTarget, selectorItem)) {
269
- /* 当前目标可以被selector所匹配到 */
270
- return true;
254
+ };
255
+ /**
256
+ * 事件回调
257
+ * @param event
258
+ */
259
+ const handlerCallBack = function (event: Event) {
260
+ let call_this: Element | undefined = void 0;
261
+ let call_event: Event | undefined = void 0;
262
+ let call_$selector: HTMLElement | undefined = void 0;
263
+ let execCallback = false;
264
+ if (selectorList.length) {
265
+ // 存在子元素选择器
266
+ // 这时候的this和target都是子元素选择器的元素
267
+ let $target: HTMLElement;
268
+ if (listenerOption.isComposedPath) {
269
+ // 可能为空
270
+ const composedPath = event.composedPath();
271
+ if (!composedPath.length && event.target) {
272
+ composedPath.push(event.target);
273
+ }
274
+ $target = composedPath[0] as HTMLElement;
275
+ } else {
276
+ $target = event.target as HTMLElement;
271
277
  }
272
- /* 在上层与主元素之间寻找可以被selector所匹配到的 */
273
- const $closestMatches = that.closest<HTMLElement>(eventTarget, selectorItem);
274
- if ($closestMatches && (<HTMLElement>totalParent)?.contains?.($closestMatches)) {
275
- eventTarget = $closestMatches;
276
- return true;
278
+ let $parent = $elItem;
279
+ if (CommonUtils.isWin($parent)) {
280
+ // window和document共用一个对象
281
+ // 这样就能处理子元素选择器无法匹配的问题
282
+ $parent = that.windowApi.document.documentElement;
277
283
  }
278
- return false;
279
- });
280
- if (findValue) {
281
- // 这里尝试使用defineProperty修改event的target值
282
- try {
283
- OriginPrototype.Object.defineProperty(event, "target", {
284
- get() {
285
- return eventTarget;
286
- },
287
- });
288
- } catch {
289
- // TODO
284
+ const findValue = selectorList.find((selectors) => {
285
+ // 判断目标元素是否匹配选择器
286
+ if (that.matches($target, selectors)) {
287
+ // 当前目标可以被selector所匹配到
288
+ return true;
289
+ }
290
+ // 在上层与主元素之间寻找可以被selector所匹配到的
291
+ const $closestMatches = that.closest<HTMLElement>($target, selectors);
292
+ if ($closestMatches && (<HTMLElement>$parent)?.contains?.($closestMatches)) {
293
+ $target = $closestMatches;
294
+ return true;
295
+ }
296
+ return false;
297
+ });
298
+ if (findValue) {
299
+ // 这里尝试使用defineProperty修改event的target值
300
+ try {
301
+ OriginPrototype.Object.defineProperty(event, "target", {
302
+ get() {
303
+ return $target;
304
+ },
305
+ });
306
+ // oxlint-disable-next-line no-empty
307
+ } catch {}
308
+ execCallback = true;
309
+ call_this = $target;
310
+ call_event = event;
311
+ call_$selector = $target;
290
312
  }
291
- listenerCallBack.call(eventTarget, event as any, eventTarget);
313
+ } else {
314
+ execCallback = true;
315
+ call_this = $elItem as Element;
316
+ call_event = event;
317
+ }
318
+ if (execCallback) {
319
+ const result = listenerCallBack.call(call_this!, call_event!, call_$selector!);
292
320
  checkOptionOnceToRemoveEventListener();
321
+ if (typeof result === "boolean" && !result) {
322
+ return false;
323
+ }
293
324
  }
294
- } else {
295
- // 这时候的this指向监听的元素
296
- listenerCallBack.call(elementItem as HTMLElement, event as any);
297
- checkOptionOnceToRemoveEventListener();
298
- }
299
- };
300
-
301
- /* 遍历事件名设置元素事件 */
302
- eventTypeList.forEach((eventName) => {
303
- elementItem.addEventListener(eventName, handlerCallBack, listenerOption);
304
- /* 获取对象上的事件 */
325
+ };
326
+ // add listener
327
+ $elItem.addEventListener(eventName, handlerCallBack, listenerOption);
328
+ // 获取对象上的事件
305
329
  const elementEvents: {
306
330
  [k: string]: DOMUtilsEventListenerOptionsAttribute[];
307
- } = Reflect.get(elementItem, GlobalData.domEventSymbol) || {};
308
- /* 初始化对象上的xx事件 */
331
+ } = Reflect.get($elItem, GlobalData.domEventSymbol) || {};
332
+ // 初始化对象上的xx事件
309
333
  elementEvents[eventName] = elementEvents[eventName] || [];
310
334
  elementEvents[eventName].push({
311
335
  selector: selectorList,
@@ -313,8 +337,8 @@ class ElementEvent extends ElementAnimate {
313
337
  handlerCallBack: handlerCallBack,
314
338
  callback: listenerCallBack,
315
339
  });
316
- /* 覆盖事件 */
317
- Reflect.set(elementItem, GlobalData.domEventSymbol, elementEvents);
340
+ // 覆盖事件
341
+ Reflect.set($elItem, GlobalData.domEventSymbol, elementEvents);
318
342
  });
319
343
  });
320
344
 
@@ -335,7 +359,7 @@ class ElementEvent extends ElementAnimate {
335
359
  /**
336
360
  * 主动触发事件
337
361
  * @param extraDetails 赋予触发的Event的额外属性,如果是Event类型,那么将自动代替默认new的Event对象
338
- * @param useDispatchToTriggerEvent 是否使用dispatchEvent来触发事件,默认true,如果为false,则直接调用callback,但是这种会让使用了$selector的没有值
362
+ * @param useDispatchToTriggerEvent 是否使用dispatchEvent来触发事件,默认true,如果为false,则直接调用callback,但是这种会让使用了`$selector`的没有值
339
363
  */
340
364
  emit: (extraDetails?: object, useDispatchToTriggerEvent?: boolean) => {
341
365
  that.emit($elList, eventTypeList, extraDetails, useDispatchToTriggerEvent);
@@ -355,7 +379,7 @@ class ElementEvent extends ElementAnimate {
355
379
  * DOMUtils.off(document.querySelector("a.xx"),"click")
356
380
  * DOMUtils.off("a.xx","click")
357
381
  */
358
- off<T extends DOMUtils_EventType>(
382
+ off<T extends DOMUtils_EventType = DOMUtils_EventType>(
359
383
  element: DOMUtilsElementEventType,
360
384
  eventType: T | T[],
361
385
  callback?: <E extends HTMLElement = HTMLElement>(this: E, event: DOMUtils_Event[T]) => void,
@@ -379,7 +403,7 @@ class ElementEvent extends ElementAnimate {
379
403
  * DOMUtils.off(document.querySelector("a.xx"),"click")
380
404
  * DOMUtils.off("a.xx","click")
381
405
  */
382
- off<T extends Event>(
406
+ off<T extends Event = Event>(
383
407
  element: DOMUtilsElementEventType,
384
408
  eventType: string | string[],
385
409
  callback?: <E extends HTMLElement = HTMLElement>(this: E, event: T) => void,
@@ -404,7 +428,7 @@ class ElementEvent extends ElementAnimate {
404
428
  * DOMUtils.off(document.querySelector("a.xx"),"click tap hover")
405
429
  * DOMUtils.off("a.xx",["click","tap","hover"])
406
430
  */
407
- off<T extends DOMUtils_EventType>(
431
+ off<T extends DOMUtils_EventType = DOMUtils_EventType>(
408
432
  element: DOMUtilsElementEventType,
409
433
  eventType: T | T[],
410
434
  selector?: string | string[] | undefined | null,
@@ -430,7 +454,7 @@ class ElementEvent extends ElementAnimate {
430
454
  * DOMUtils.off(document.querySelector("a.xx"),"click tap hover")
431
455
  * DOMUtils.off("a.xx",["click","tap","hover"])
432
456
  */
433
- off<T extends Event>(
457
+ off<T extends Event = Event>(
434
458
  element: DOMUtilsElementEventType,
435
459
  eventType: string | string[],
436
460
  selector?: string | string[] | undefined | null,
@@ -442,7 +466,7 @@ class ElementEvent extends ElementAnimate {
442
466
  array: DOMUtilsEventListenerOptionsAttribute[]
443
467
  ) => boolean
444
468
  ): void;
445
- off<T extends Event>(
469
+ off<T extends Event = Event>(
446
470
  element: HTMLElement | string | NodeList | HTMLElement[] | Window | Document | Element | null | typeof globalThis,
447
471
  eventType: DOMUtils_EventType | DOMUtils_EventType[] | string | string[],
448
472
  selector:
@@ -475,7 +499,7 @@ class ElementEvent extends ElementAnimate {
475
499
  * @param startIndex
476
500
  * @param option
477
501
  */
478
- function getOption(args1: IArguments, startIndex: number, option: EventListenerOptions) {
502
+ const getOption = function (args1: IArguments, startIndex: number, option: EventListenerOptions) {
479
503
  const currentParam: EventListenerOptions | boolean = args1[startIndex];
480
504
  if (typeof currentParam === "boolean") {
481
505
  option.capture = currentParam;
@@ -483,7 +507,7 @@ class ElementEvent extends ElementAnimate {
483
507
  option.capture = currentParam.capture;
484
508
  }
485
509
  return option;
486
- }
510
+ };
487
511
  const that = this;
488
512
  // eslint-disable-next-line prefer-rest-params
489
513
  const args = arguments;
@@ -516,7 +540,11 @@ class ElementEvent extends ElementAnimate {
516
540
  /**
517
541
  * 事件的回调函数
518
542
  */
519
- let listenerCallBack: (this: HTMLElement, event: T, $selector: HTMLElement) => void = callback as any;
543
+ let listenerCallBack: (this: HTMLElement, event: T, $selector: HTMLElement) => void = callback as (
544
+ this: HTMLElement,
545
+ event: Event,
546
+ $selector?: HTMLElement
547
+ ) => void | boolean;
520
548
 
521
549
  /**
522
550
  * 事件的配置
@@ -542,23 +570,26 @@ class ElementEvent extends ElementAnimate {
542
570
  ) => boolean;
543
571
  }
544
572
  $elList.forEach(($elItem) => {
545
- /* 获取对象上的事件 */
573
+ // 获取对象上的事件
546
574
  const elementEvents: {
547
575
  [key: string]: DOMUtilsEventListenerOptionsAttribute[];
548
576
  } = Reflect.get($elItem, GlobalData.domEventSymbol) || {};
549
577
  eventTypeList.forEach((eventName) => {
550
578
  const handlers = elementEvents[eventName] || [];
551
- const filterHandler = typeof filter === "function" ? handlers.filter(filter) : handlers;
552
- for (let index = 0; index < filterHandler.length; index++) {
553
- const handler = filterHandler[index];
579
+ // 过滤出需要删除的事件
580
+ const handlersFiltered = typeof filter === "function" ? handlers.filter(filter) : handlers;
581
+ for (let index = 0; index < handlersFiltered.length; index++) {
582
+ const handler = handlersFiltered[index];
583
+ // 过滤出的事件再根据下面的条件进行判断处理移除
584
+ // 1. callback内存地址必须相同
585
+ // 2. selector必须相同
586
+ // 3. option.capture必须相同
554
587
  let flag = true;
555
588
  if (flag && listenerCallBack && handler.callback !== listenerCallBack) {
556
- // callback不同
557
589
  flag = false;
558
590
  }
559
591
  if (flag && selectorList.length && Array.isArray(handler.selector)) {
560
592
  if (JSON.stringify(handler.selector) !== JSON.stringify(selectorList)) {
561
- // 子元素选择器不同
562
593
  flag = false;
563
594
  }
564
595
  }
@@ -567,19 +598,19 @@ class ElementEvent extends ElementAnimate {
567
598
  typeof handler.option.capture === "boolean" &&
568
599
  listenerOption.capture !== handler.option.capture
569
600
  ) {
570
- // 事件的配置项不同
571
601
  flag = false;
572
602
  }
573
603
  if (flag) {
574
604
  $elItem.removeEventListener(eventName, handler.handlerCallBack, handler.option);
575
- const findIndex = handlers.findIndex((item) => item === handler);
576
- if (findIndex !== -1) {
577
- handlers.splice(findIndex, 1);
605
+ for (let i = handlers.length - 1; i >= 0; i--) {
606
+ if (handlers[i] === handler) {
607
+ handlers.splice(i, 1);
608
+ }
578
609
  }
579
610
  }
580
611
  }
581
612
  if (handlers.length === 0) {
582
- /* 如果没有任意的handler,那么删除该属性 */
613
+ // 如果没有任意的handler,那么删除该属性
583
614
  CommonUtils.delete(elementEvents, eventType);
584
615
  }
585
616
  });
@@ -1578,7 +1609,7 @@ class ElementEvent extends ElementAnimate {
1578
1609
  * })
1579
1610
  */
1580
1611
  preventEvent(
1581
- $el: HTMLElement,
1612
+ $el: Element | Document | ShadowRoot,
1582
1613
  eventNameList: string | string[],
1583
1614
  option?: {
1584
1615
  /** (可选)是否捕获,默认false */
@@ -1609,7 +1640,7 @@ class ElementEvent extends ElementAnimate {
1609
1640
  * })
1610
1641
  */
1611
1642
  preventEvent(
1612
- $el: HTMLElement,
1643
+ $el: Element | Document | ShadowRoot,
1613
1644
  eventNameList: string | string[],
1614
1645
  selector: string | string[] | null | undefined,
1615
1646
  option?: {
@@ -1627,11 +1658,11 @@ class ElementEvent extends ElementAnimate {
1627
1658
  * 阻止事件的默认行为发生,并阻止事件传播
1628
1659
  */
1629
1660
  const stopEvent = (event: Event, onlyStopPropagation?: boolean) => {
1661
+ // 停止事件的传播,阻止它继续向更上层的元素冒泡,事件将不会再传播给其他的元素
1662
+ event?.stopPropagation();
1663
+ // 阻止事件传播,并且还能阻止元素上的其他事件处理程序被触发
1664
+ event?.stopImmediatePropagation();
1630
1665
  if (typeof onlyStopPropagation === "boolean" && onlyStopPropagation) {
1631
- // 停止事件的传播,阻止它继续向更上层的元素冒泡,事件将不会再传播给其他的元素
1632
- event?.stopPropagation();
1633
- // 阻止事件传播,并且还能阻止元素上的其他事件处理程序被触发
1634
- event?.stopImmediatePropagation();
1635
1666
  return;
1636
1667
  }
1637
1668
  // 阻止事件的默认行为发生。例如,当点击一个链接时,浏览器会默认打开链接的URL,或者在输入框内输入文字
@@ -1643,7 +1674,7 @@ class ElementEvent extends ElementAnimate {
1643
1674
  const onlyStopPropagation: boolean = args[1];
1644
1675
  return stopEvent(args[0], onlyStopPropagation);
1645
1676
  } else {
1646
- const $el: HTMLElement = args[0];
1677
+ const $el: Element | Document | ShadowRoot = args[0];
1647
1678
  let eventNameList: string | string[] = args[1];
1648
1679
  let selector: string | string[] | null | undefined = void 0;
1649
1680
  let capture = false;
@@ -369,6 +369,7 @@ export declare type DOMUtilsEventListenerOption = AddEventListenerOptions & {
369
369
  * 是否使用 event.composedPath() 来代替 event.target
370
370
  *
371
371
  * 一般用于设置了selector参数
372
+ * @default false
372
373
  */
373
374
  isComposedPath?: boolean;
374
375
  };