UNPKG

@whitesev/pops

Version:

弹窗库

1,745 lines (1,729 loc) 79 kB
import type { ParseHTMLReturnType, PopsDOMUtils_EventType, PopsDOMUtilsCreateElementAttributesMap, PopsDOMUtilsEventListenerOption, PopsDOMUtilsEventListenerOptionsAttribute, PopsDOMUtils_Event, PopsDOMUtilsElementEventType, } from "../types/PopsDOMUtilsEventType"; import { SymbolEvents } from "../Config"; import { OriginPrototype, PopsCore } from "../Core"; import { popsUtils } from "./PopsUtils"; import { PopsSafeUtils } from "./PopsSafeUtils"; class PopsDOMUtilsEvent { /** * 绑定事件 * @param element 需要绑定的元素|元素数组|window * @param eventType 需要监听的事件 * @param callback 绑定事件触发的回调函数 * @param option * + capture 表示事件是否在捕获阶段触发。默认为false,即在冒泡阶段触发 * + once 表示事件是否只触发一次。默认为false * + passive 表示事件监听器是否不会调用preventDefault()。默认为false * @example * // 监听元素a.xx的click事件 * DOMUtils.on(document.querySelector("a.xx"),"click",(event)=>{ * console.log("事件触发",event) * }) * DOMUtils.on("a.xx","click",(event)=>{ * console.log("事件触发",event) * }) */ on<T extends PopsDOMUtils_EventType>( element: PopsDOMUtilsElementEventType, eventType: T | T[], callback: (this: HTMLElement, event: PopsDOMUtils_Event[T]) => void, option?: PopsDOMUtilsEventListenerOption | boolean ): void; /** * 绑定事件 * @param element 需要绑定的元素|元素数组|window * @param eventType 需要监听的事件 * @param callback 绑定事件触发的回调函数 * @param option * + capture 表示事件是否在捕获阶段触发。默认为false,即在冒泡阶段触发 * + once 表示事件是否只触发一次。默认为false * + passive 表示事件监听器是否不会调用preventDefault()。默认为false * @example * // 监听元素a.xx的click事件 * DOMUtils.on(document.querySelector("a.xx"),"click",(event)=>{ * console.log("事件触发",event) * }) * DOMUtils.on("a.xx","click",(event)=>{ * console.log("事件触发",event) * }) */ on<T extends Event>( element: PopsDOMUtilsElementEventType, eventType: string | string[], callback: (this: HTMLElement, event: T) => void, option?: PopsDOMUtilsEventListenerOption | boolean ): void; /** * 绑定事件 * @param element 需要绑定的元素|元素数组|window * @param eventType 需要监听的事件 * @param selector 子元素选择器 * @param callback 绑定事件触发的回调函数 * @param option * + capture 表示事件是否在捕获阶段触发。默认为false,即在冒泡阶段触发 * + once 表示事件是否只触发一次。默认为false * + passive 表示事件监听器是否不会调用preventDefault()。默认为false * @example * // 监听元素a.xx的click、tap、hover事件 * DOMUtils.on(document.querySelector("a.xx"),"click tap hover",(event, selectorTarget)=>{ * console.log("事件触发", event, selectorTarget) * }) * DOMUtils.on("a.xx",["click","tap","hover"],(event, selectorTarget)=>{ * console.log("事件触发", event, selectorTarget) * }) * @example * // 监听全局document下的子元素a.xx的click事件 * DOMUtils.on(document,"click tap hover","a.xx",(event, selectorTarget)=>{ * console.log("事件触发", event, selectorTarget) * }) */ on<T extends PopsDOMUtils_EventType>( element: PopsDOMUtilsElementEventType, eventType: T | T[], selector: string | string[] | undefined | null, callback: ( this: HTMLElement, event: PopsDOMUtils_Event[T], selectorTarget: HTMLElement ) => void, option?: PopsDOMUtilsEventListenerOption | boolean ): void; /** * 绑定事件 * @param element 需要绑定的元素|元素数组|window * @param eventType 需要监听的事件 * @param selector 子元素选择器 * @param callback 绑定事件触发的回调函数 * @param option * + capture 表示事件是否在捕获阶段触发。默认为false,即在冒泡阶段触发 * + once 表示事件是否只触发一次。默认为false * + passive 表示事件监听器是否不会调用preventDefault()。默认为false * @example * // 监听元素a.xx的click、tap、hover事件 * DOMUtils.on(document.querySelector("a.xx"),"click tap hover",(event, selectorTarget)=>{ * console.log("事件触发", event, selectorTarget) * }) * DOMUtils.on("a.xx",["click","tap","hover"],(event, selectorTarget)=>{ * console.log("事件触发", event, selectorTarget) * }) * @example * // 监听全局document下的子元素a.xx的click事件 * DOMUtils.on(document,"click tap hover","a.xx",(event, selectorTarget)=>{ * console.log("事件触发", event, selectorTarget) * }) */ on<T extends Event>( element: PopsDOMUtilsElementEventType, eventType: string | string[], selector: string | string[] | undefined | null, callback: ( this: HTMLElement, event: T, selectorTarget: HTMLElement ) => void, option?: PopsDOMUtilsEventListenerOption | boolean ): void; on<T extends Event>( element: | HTMLElement | string | NodeList | HTMLElement[] | Window | Document | Element | null | typeof globalThis, eventType: | PopsDOMUtils_EventType | PopsDOMUtils_EventType[] | string | string[], selector: | string | string[] | undefined | ((this: HTMLElement, event: T, selectorTarget: HTMLElement) => void) | null, callback?: | ((this: HTMLElement, event: T, selectorTarget: HTMLElement) => void) | PopsDOMUtilsEventListenerOption | boolean, option?: PopsDOMUtilsEventListenerOption | boolean ) { /** * 获取option配置 * @param args * @param startIndex * @param option */ function getOption( args: IArguments, startIndex: number, option: PopsDOMUtilsEventListenerOption ) { let currentParam = args[startIndex]; if (typeof currentParam === "boolean") { option.capture = currentParam; if (typeof args[startIndex + 1] === "boolean") { option.once = args[startIndex + 1]; } if (typeof args[startIndex + 2] === "boolean") { option.passive = args[startIndex + 2]; } } else if ( typeof currentParam === "object" && ("capture" in currentParam || "once" in currentParam || "passive" in currentParam || "isComposedPath" in currentParam) ) { option.capture = currentParam.capture; option.once = currentParam.once; option.passive = currentParam.passive; option.isComposedPath = currentParam.isComposedPath; } return option; } let DOMUtilsContext = this; let args = arguments; if (typeof element === "string") { element = DOMUtilsContext.selectorAll(element); } if (element == null) { return; } let elementList: HTMLElement[] = []; if (element instanceof NodeList || Array.isArray(element)) { element = element as HTMLElement[]; elementList = [...element]; } else { elementList.push(element as HTMLElement); } // 事件名 let eventTypeList: string[] = []; if (Array.isArray(eventType)) { eventTypeList = eventTypeList.concat( eventType.filter( (eventTypeItem) => typeof eventTypeItem === "string" && eventTypeItem.toString() !== "" ) ); } else if (typeof eventType === "string") { eventTypeList = eventTypeList.concat( eventType.split(" ").filter((eventTypeItem) => eventTypeItem !== "") ); } // 子元素选择器 let selectorList: string[] = []; if (Array.isArray(selector)) { selectorList = selectorList.concat( selector.filter( (selectorItem) => typeof selectorItem === "string" && selectorItem.toString() !== "" ) ); } else if (typeof selector === "string") { selectorList.push(selector); } // 事件回调 let listenerCallBack: ( this: HTMLElement, event: Event, selectorTarget?: HTMLElement ) => void = callback as any; // 事件配置 let listenerOption: PopsDOMUtilsEventListenerOption = { capture: false, once: false, passive: false, isComposedPath: false, }; if (typeof selector === "function") { // 这是为没有selector的情况 // 那么它就是callback listenerCallBack = selector as any; listenerOption = getOption(args, 3, listenerOption); } else { // 这是存在selector的情况 listenerOption = getOption(args, 4, listenerOption); } /** * 如果是once,那么删除该监听和元素上的事件和监听 */ function checkOptionOnceToRemoveEventListener() { if (listenerOption.once) { DOMUtilsContext.off( element, eventType as any, selector as any, callback as any, option ); } } elementList.forEach((elementItem) => { /** * 事件回调 * @param event */ function domUtilsEventCallBack(event: Event) { if (selectorList.length) { /* 存在子元素选择器 */ // 这时候的this和target都是子元素选择器的元素 let eventTarget = listenerOption.isComposedPath ? (event.composedPath()[0] as HTMLElement) : (event.target as HTMLElement); let totalParent = elementItem; if (popsUtils.isWin(totalParent)) { if (totalParent === (PopsCore.document as any as HTMLElement)) { totalParent = PopsCore.document.documentElement; } } let findValue = selectorList.find((selectorItem) => { // 判断目标元素是否匹配选择器 if (DOMUtilsContext.matches(eventTarget, selectorItem)) { /* 当前目标可以被selector所匹配到 */ return true; } /* 在上层与主元素之间寻找可以被selector所匹配到的 */ let $closestMatches = DOMUtilsContext.closest<HTMLElement>( eventTarget, selectorItem ); if ($closestMatches && totalParent?.contains($closestMatches)) { eventTarget = $closestMatches; return true; } return false; }); if (findValue) { // 这里尝试使用defineProperty修改event的target值 try { OriginPrototype.Object.defineProperty(event, "target", { get() { return eventTarget; }, }); } catch (error) {} listenerCallBack.call(eventTarget, event as any, eventTarget); checkOptionOnceToRemoveEventListener(); } } else { // 这时候的this指向监听的元素 listenerCallBack.call(elementItem, event as any); checkOptionOnceToRemoveEventListener(); } } /* 遍历事件名设置元素事件 */ eventTypeList.forEach((eventName) => { elementItem.addEventListener( eventName, domUtilsEventCallBack, listenerOption ); /* 获取对象上的事件 */ let elementEvents: { [k: string]: PopsDOMUtilsEventListenerOptionsAttribute[]; } = Reflect.get(elementItem, SymbolEvents) || {}; /* 初始化对象上的xx事件 */ elementEvents[eventName] = elementEvents[eventName] || []; elementEvents[eventName].push({ selector: selectorList, option: listenerOption, callback: domUtilsEventCallBack, originCallBack: listenerCallBack, }); /* 覆盖事件 */ Reflect.set(elementItem, SymbolEvents, elementEvents); }); }); } /** * 取消绑定事件 * @param element 需要取消绑定的元素|元素数组 * @param eventType 需要取消监听的事件 * @param callback 通过DOMUtils.on绑定的事件函数 * @param option * + capture 如果在添加事件监听器时指定了useCapture为true,则在移除事件监听器时也必须指定为true * @param filter (可选)过滤函数,对元素属性上的事件进行过滤出想要删除的事件 * @example * // 取消监听元素a.xx所有的click事件 * DOMUtils.off(document.querySelector("a.xx"),"click") * DOMUtils.off("a.xx","click") */ off<T extends PopsDOMUtils_EventType>( element: PopsDOMUtilsElementEventType, eventType: T | T[], callback?: (this: HTMLElement, event: PopsDOMUtils_Event[T]) => void, option?: EventListenerOptions | boolean, filter?: ( value: PopsDOMUtilsEventListenerOptionsAttribute, index: number, array: PopsDOMUtilsEventListenerOptionsAttribute[] ) => boolean ): void; /** * 取消绑定事件 * @param element 需要取消绑定的元素|元素数组 * @param eventType 需要取消监听的事件 * @param callback 通过DOMUtils.on绑定的事件函数 * @param option * + capture 如果在添加事件监听器时指定了useCapture为true,则在移除事件监听器时也必须指定为true * @param filter (可选)过滤函数,对元素属性上的事件进行过滤出想要删除的事件 * @example * // 取消监听元素a.xx的click事件 * DOMUtils.off(document.querySelector("a.xx"),"click") * DOMUtils.off("a.xx","click") */ off<T extends Event>( element: PopsDOMUtilsElementEventType, eventType: string | string[], callback?: (this: HTMLElement, event: T) => void, option?: EventListenerOptions | boolean, filter?: ( value: PopsDOMUtilsEventListenerOptionsAttribute, index: number, array: PopsDOMUtilsEventListenerOptionsAttribute[] ) => boolean ): void; /** * 取消绑定事件 * @param element 需要取消绑定的元素|元素数组 * @param eventType 需要取消监听的事件 * @param selector 子元素选择器 * @param callback 通过DOMUtils.on绑定的事件函数 * @param option * + capture 如果在添加事件监听器时指定了useCapture为true,则在移除事件监听器时也必须指定为true * @param filter (可选)过滤函数,对元素属性上的事件进行过滤出想要删除的事件 * @example * // 取消监听元素a.xx的click、tap、hover事件 * DOMUtils.off(document.querySelector("a.xx"),"click tap hover") * DOMUtils.off("a.xx",["click","tap","hover"]) */ off<T extends PopsDOMUtils_EventType>( element: PopsDOMUtilsElementEventType, eventType: T | T[], selector?: string | string[] | undefined | null, callback?: ( this: HTMLElement, event: PopsDOMUtils_Event[T], selectorTarget: HTMLElement ) => void, option?: EventListenerOptions | boolean, filter?: ( value: PopsDOMUtilsEventListenerOptionsAttribute, index: number, array: PopsDOMUtilsEventListenerOptionsAttribute[] ) => boolean ): void; /** * 取消绑定事件 * @param element 需要取消绑定的元素|元素数组 * @param eventType 需要取消监听的事件 * @param selector 子元素选择器 * @param callback 通过DOMUtils.on绑定的事件函数 * @param option * + capture 如果在添加事件监听器时指定了useCapture为true,则在移除事件监听器时也必须指定为true * @param filter (可选)过滤函数,对元素属性上的事件进行过滤出想要删除的事件 * @example * // 取消监听元素a.xx的click、tap、hover事件 * DOMUtils.off(document.querySelector("a.xx"),"click tap hover") * DOMUtils.off("a.xx",["click","tap","hover"]) */ off<T extends Event>( element: PopsDOMUtilsElementEventType, eventType: string | string[], selector?: string | string[] | undefined | null, callback?: ( this: HTMLElement, event: T, selectorTarget: HTMLElement ) => void, option?: EventListenerOptions | boolean, filter?: ( value: PopsDOMUtilsEventListenerOptionsAttribute, index: number, array: PopsDOMUtilsEventListenerOptionsAttribute[] ) => boolean ): void; off<T extends Event>( element: | HTMLElement | string | NodeList | HTMLElement[] | Window | Document | Element | null | typeof globalThis, eventType: | PopsDOMUtils_EventType | PopsDOMUtils_EventType[] | string | string[], selector: | string | string[] | undefined | ((this: HTMLElement, event: T, selectorTarget: HTMLElement) => void) | null, callback?: | ((this: HTMLElement, event: T, selectorTarget: HTMLElement) => void) | EventListenerOptions | boolean, option?: | EventListenerOptions | boolean | (( value: PopsDOMUtilsEventListenerOptionsAttribute, index: number, array: PopsDOMUtilsEventListenerOptionsAttribute[] ) => boolean), filter?: ( value: PopsDOMUtilsEventListenerOptionsAttribute, index: number, array: PopsDOMUtilsEventListenerOptionsAttribute[] ) => boolean ) { /** * 获取option配置 * @param args1 * @param startIndex * @param option */ function getOption( args1: IArguments, startIndex: number, option: EventListenerOptions ) { let currentParam: EventListenerOptions | boolean = args1[startIndex]; if (typeof currentParam === "boolean") { option.capture = currentParam; } else if ( typeof currentParam === "object" && "capture" in currentParam ) { option.capture = currentParam.capture; } return option; } let DOMUtilsContext = this; let args = arguments; if (typeof element === "string") { element = DOMUtilsContext.selectorAll(element); } if (element == null) { return; } let elementList: HTMLElement[] = []; if (element instanceof NodeList || Array.isArray(element)) { element = element as HTMLElement[]; elementList = [...element]; } else { elementList.push(element as HTMLElement); } let eventTypeList: string[] = []; if (Array.isArray(eventType)) { eventTypeList = eventTypeList.concat( eventType.filter( (eventTypeItem) => typeof eventTypeItem === "string" && eventTypeItem.toString() !== "" ) ); } else if (typeof eventType === "string") { eventTypeList = eventTypeList.concat( eventType.split(" ").filter((eventTypeItem) => eventTypeItem !== "") ); } // 子元素选择器 let selectorList: string[] = []; if (Array.isArray(selector)) { selectorList = selectorList.concat( selector.filter( (selectorItem) => typeof selectorItem === "string" && selectorItem.toString() !== "" ) ); } else if (typeof selector === "string") { selectorList.push(selector); } /** * 事件的回调函数 */ let listenerCallBack: ( this: HTMLElement, event: T, selectorTarget: HTMLElement ) => void = callback as any; /** * 事件的配置 */ let listenerOption: EventListenerOptions = { capture: false, }; if (typeof selector === "function") { // 这是为没有selector的情况 // 那么它就是callback listenerCallBack = selector; listenerOption = getOption(args, 3, listenerOption); } else { // 这是存在selector的情况 listenerOption = getOption(args, 4, listenerOption); } // 是否移除所有事件 let isRemoveAll = false; if (args.length === 2) { // 目标函数、事件名 isRemoveAll = true; } else if ( (args.length === 3 && typeof args[2] === "string") || Array.isArray(args[2]) ) { // 目标函数、事件名、子元素选择器 isRemoveAll = true; } elementList.forEach((elementItem) => { /* 获取对象上的事件 */ let elementEvents: { [key: string]: PopsDOMUtilsEventListenerOptionsAttribute[]; } = Reflect.get(elementItem, SymbolEvents) || {}; eventTypeList.forEach((eventName) => { let handlers = elementEvents[eventName] || []; if (typeof filter === "function") { handlers = handlers.filter(filter); } for (let index = 0; index < handlers.length; index++) { let handler = handlers[index]; let flag = true; if ( flag && listenerCallBack && handler.originCallBack !== listenerCallBack ) { // callback不同 flag = false; } if (flag && selectorList.length && Array.isArray(handler.selector)) { if ( JSON.stringify(handler.selector) !== JSON.stringify(selectorList) ) { // 子元素选择器不同 flag = false; } } if (flag && listenerOption.capture !== handler.option.capture) { // 事件的配置项不同 flag = false; } if (flag || isRemoveAll) { elementItem.removeEventListener( eventName, handler.callback, handler.option ); handlers.splice(index--, 1); } } if (handlers.length === 0) { /* 如果没有任意的handler,那么删除该属性 */ popsUtils.delete(elementEvents, eventType); } }); Reflect.set(elementItem, SymbolEvents, elementEvents); }); } /** * 取消绑定所有的事件 * @param element 需要取消绑定的元素|元素数组 * @param eventType (可选)需要取消监听的事件 */ offAll(element: PopsDOMUtilsElementEventType, eventType?: string): void; /** * 取消绑定所有的事件 * @param element 需要取消绑定的元素|元素数组 * @param eventType (可选)需要取消监听的事件 */ offAll( element: PopsDOMUtilsElementEventType, eventType?: PopsDOMUtils_EventType | PopsDOMUtils_EventType[] ): void; /** * 取消绑定所有的事件 * @param element 需要取消绑定的元素|元素数组 * @param eventType (可选)需要取消监听的事件 */ offAll( element: PopsDOMUtilsElementEventType, eventType?: PopsDOMUtils_EventType | PopsDOMUtils_EventType[] | string ) { if (typeof element === "string") { element = PopsCore.document.querySelectorAll(element); } if (element == null) { return; } let elementList: HTMLElement[] = []; if (element instanceof NodeList || Array.isArray(element)) { elementList = [...(element as HTMLElement[])]; } else { elementList.push(element as HTMLElement); } let eventTypeList: string[] = []; if (Array.isArray(eventType)) { eventTypeList = eventTypeList.concat(eventType as string[]); } else if (typeof eventType === "string") { eventTypeList = eventTypeList.concat(eventType.split(" ")); } elementList.forEach((elementItem) => { Object.getOwnPropertySymbols(elementItem).forEach((__symbolEvents) => { if (!__symbolEvents.toString().startsWith("Symbol(events_")) { return; } let elementEvents = (elementItem as any)[__symbolEvents] || {}; let iterEventNameList = eventTypeList.length ? eventTypeList : Object.keys(elementEvents); iterEventNameList.forEach((eventName) => { let handlers = elementEvents[eventName]; if (!handlers) { return; } for (const handler of handlers) { elementItem.removeEventListener(eventName, handler.callback, { capture: handler["option"]["capture"], }); } popsUtils.delete((elementItem as any)[__symbolEvents], eventName); }); }); }); } /** * 等待文档加载完成后执行指定的函数 * @param callback 需要执行的函数 * @example * DOMUtils.ready(function(){ * console.log("文档加载完毕") * }) */ ready<T extends Function>(callback: T) { if (typeof callback !== "function") { return; } /** * 检测文档是否加载完毕 */ function checkDOMReadyState() { try { if ( document.readyState === "complete" || (document.readyState !== "loading" && !(document.documentElement as any).doScroll) ) { return true; } else { return false; } } catch (error) { return false; } } /** * 成功加载完毕后触发的回调函数 */ function completed() { removeDomReadyListener(); callback(); } let targetList = [ { target: PopsCore.document, eventType: "DOMContentLoaded", callback: completed, }, { target: PopsCore.window, eventType: "load", callback: completed, }, ]; /** * 添加监听 */ function addDomReadyListener() { for (let index = 0; index < targetList.length; index++) { let item = targetList[index]; item.target.addEventListener(item.eventType, item.callback); } } /** * 移除监听 */ function removeDomReadyListener() { for (let index = 0; index < targetList.length; index++) { let item = targetList[index]; item.target.removeEventListener(item.eventType, item.callback); } } if (checkDOMReadyState()) { /* 检查document状态 */ popsUtils.setTimeout(callback, 0); } else { /* 添加监听 */ addDomReadyListener(); } } /** * 主动触发事件 * @param element 需要触发的元素|元素数组|window * @param eventType 需要触发的事件 * @param details 赋予触发的Event的额外属性,如果是Event类型,那么将自动代替默认new的Event对象 * @param useDispatchToTriggerEvent 是否使用dispatchEvent来触发事件,默认true * @example * // 触发元素a.xx的click事件 * DOMUtils.trigger(document.querySelector("a.xx"),"click") * DOMUtils.trigger("a.xx","click") * // 触发元素a.xx的click、tap、hover事件 * DOMUtils.trigger(document.querySelector("a.xx"),"click tap hover") * DOMUtils.trigger("a.xx",["click","tap","hover"]) */ trigger( element: HTMLElement | string | NodeList | any[] | Window | Document, eventType: string | string[], details?: object, useDispatchToTriggerEvent?: boolean ): void; /** * 主动触发事件 * @param element 需要触发的元素|元素数组|window * @param eventType 需要触发的事件 * @param details 赋予触发的Event的额外属性,如果是Event类型,那么将自动代替默认new的Event对象 * @param useDispatchToTriggerEvent 是否使用dispatchEvent来触发事件,默认true * @example * // 触发元素a.xx的click事件 * DOMUtils.trigger(document.querySelector("a.xx"),"click") * DOMUtils.trigger("a.xx","click") * // 触发元素a.xx的click、tap、hover事件 * DOMUtils.trigger(document.querySelector("a.xx"),"click tap hover") * DOMUtils.trigger("a.xx",["click","tap","hover"]) */ trigger( element: HTMLElement | string | NodeList | any[] | Window | Document, eventType: PopsDOMUtils_EventType | PopsDOMUtils_EventType[], details?: object, useDispatchToTriggerEvent?: boolean ): void; /** * 主动触发事件 * @param element 需要触发的元素|元素数组|window * @param eventType 需要触发的事件 * @param details 赋予触发的Event的额外属性,如果是Event类型,那么将自动代替默认new的Event对象 * @param useDispatchToTriggerEvent 是否使用dispatchEvent来触发事件,默认true * @example * // 触发元素a.xx的click事件 * DOMUtils.trigger(document.querySelector("a.xx"),"click") * DOMUtils.trigger("a.xx","click") * // 触发元素a.xx的click、tap、hover事件 * DOMUtils.trigger(document.querySelector("a.xx"),"click tap hover") * DOMUtils.trigger("a.xx",["click","tap","hover"]) */ trigger( element: HTMLElement | string | NodeList | any[] | Window | Document, eventType: PopsDOMUtils_EventType | PopsDOMUtils_EventType[] | string, details?: object, useDispatchToTriggerEvent: boolean = true ) { if (typeof element === "string") { element = PopsCore.document.querySelector(element) as HTMLElement; } if (element == null) { return; } let elementList = []; if (element instanceof NodeList || Array.isArray(element)) { element = element as HTMLElement[]; elementList = [...element]; } else { elementList = [element]; } let eventTypeList: string[] = []; if (Array.isArray(eventType)) { eventTypeList = eventType as string[]; } else if (typeof eventType === "string") { eventTypeList = eventType.split(" "); } elementList.forEach((elementItem) => { /* 获取对象上的事件 */ let events = elementItem[SymbolEvents] || {}; eventTypeList.forEach((_eventType_) => { let event: Event = null as any; if (details && details instanceof Event) { event = details; } else { event = new Event(_eventType_); if (details) { Object.keys(details).forEach((keyName) => { (event as any)[keyName] = (details as any)[keyName]; }); } } if (useDispatchToTriggerEvent == false && _eventType_ in events) { events[_eventType_].forEach((eventsItem: any) => { eventsItem.callback(event); }); } else { elementItem.dispatchEvent(event); } }); }); } /** * 绑定或触发元素的click事件 * @param element 目标元素 * @param handler (可选)事件处理函数 * @param details (可选)赋予触发的Event的额外属性 * @param useDispatchToTriggerEvent (可选)是否使用dispatchEvent来触发事件,默认true * @example * // 触发元素a.xx的click事件 * DOMUtils.click(document.querySelector("a.xx")) * DOMUtils.click("a.xx") * DOMUtils.click("a.xx",function(){ * console.log("触发click事件成功") * }) * */ click( element: HTMLElement | string | Window, handler?: (event: PopsDOMUtils_Event["click"]) => void, details?: any, useDispatchToTriggerEvent?: boolean ) { let DOMUtilsContext = this; if (typeof element === "string") { element = PopsCore.document.querySelector(element) as HTMLElement; } if (element == null) { return; } if (handler == null) { DOMUtilsContext.trigger( element, "click", details, useDispatchToTriggerEvent ); } else { DOMUtilsContext.on(element, "click", null, handler); } } /** * 绑定或触发元素的blur事件 * @param element 目标元素 * @param handler (可选)事件处理函数 * @param details (可选)赋予触发的Event的额外属性 * @param useDispatchToTriggerEvent (可选)是否使用dispatchEvent来触发事件,默认true * @example * // 触发元素a.xx的blur事件 * DOMUtils.blur(document.querySelector("a.xx")) * DOMUtils.blur("a.xx") * DOMUtils.blur("a.xx",function(){ * console.log("触发blur事件成功") * }) * */ blur( element: HTMLElement | string | Window, handler?: (event: PopsDOMUtils_Event["blur"]) => void, details?: object, useDispatchToTriggerEvent?: boolean ) { let DOMUtilsContext = this; if (typeof element === "string") { element = PopsCore.document.querySelector(element) as HTMLElement; } if (element == null) { return; } if (handler === null) { DOMUtilsContext.trigger( element, "blur", details, useDispatchToTriggerEvent ); } else { DOMUtilsContext.on( element, "blur", null, handler as (event: Event) => void ); } } /** * 绑定或触发元素的focus事件 * @param element 目标元素 * @param handler (可选)事件处理函数 * @param details (可选)赋予触发的Event的额外属性 * @param useDispatchToTriggerEvent (可选)是否使用dispatchEvent来触发事件,默认true * @example * // 触发元素a.xx的focus事件 * DOMUtils.focus(document.querySelector("a.xx")) * DOMUtils.focus("a.xx") * DOMUtils.focus("a.xx",function(){ * console.log("触发focus事件成功") * }) * */ focus( element: HTMLElement | string | Window, handler?: (event: PopsDOMUtils_Event["focus"]) => void, details?: object, useDispatchToTriggerEvent?: boolean ) { let DOMUtilsContext = this; if (typeof element === "string") { element = PopsCore.document.querySelector(element) as HTMLElement; } if (element == null) { return; } if (handler == null) { DOMUtilsContext.trigger( element, "focus", details, useDispatchToTriggerEvent ); } else { DOMUtilsContext.on(element, "focus", null, handler); } } /** * 当鼠标移入或移出元素时触发事件 * @param element 当前元素 * @param handler 事件处理函数 * @param option 配置 * @example * // 监听a.xx元素的移入或移出 * DOMUtils.hover(document.querySelector("a.xx"),()=>{ * console.log("移入/移除"); * }) * DOMUtils.hover("a.xx",()=>{ * console.log("移入/移除"); * }) */ hover( element: HTMLElement | string, handler: (event: PopsDOMUtils_Event["hover"]) => void, option?: boolean | AddEventListenerOptions ) { let DOMUtilsContext = this; if (typeof element === "string") { element = PopsCore.document.querySelector(element) as HTMLElement; } if (element == null) { return; } DOMUtilsContext.on(element, "mouseenter", null, handler, option); DOMUtilsContext.on(element, "mouseleave", null, handler, option); } /** * 当按键松开时触发事件 * keydown - > keypress - > keyup * @param target 当前元素 * @param handler 事件处理函数 * @param option 配置 * @example * // 监听a.xx元素的按键松开 * DOMUtils.keyup(document.querySelector("a.xx"),()=>{ * console.log("按键松开"); * }) * DOMUtils.keyup("a.xx",()=>{ * console.log("按键松开"); * }) */ keyup( target: HTMLElement | string | Window | typeof globalThis, handler: (event: PopsDOMUtils_Event["keyup"]) => void, option?: boolean | AddEventListenerOptions ) { let DOMUtilsContext = this; if (target == null) { return; } if (typeof target === "string") { target = PopsCore.document.querySelector(target) as HTMLElement; } DOMUtilsContext.on(target, "keyup", null, handler, option); } /** * 当按键按下时触发事件 * keydown - > keypress - > keyup * @param target 目标 * @param handler 事件处理函数 * @param option 配置 * @example * // 监听a.xx元素的按键按下 * DOMUtils.keydown(document.querySelector("a.xx"),()=>{ * console.log("按键按下"); * }) * DOMUtils.keydown("a.xx",()=>{ * console.log("按键按下"); * }) */ keydown( target: HTMLElement | Window | typeof globalThis | string, handler: (event: PopsDOMUtils_Event["keydown"]) => void, option?: boolean | AddEventListenerOptions ) { let DOMUtilsContext = this; if (target == null) { return; } if (typeof target === "string") { target = PopsCore.document.querySelector(target) as HTMLElement; } DOMUtilsContext.on(target, "keydown", null, handler, option); } /** * 当按键按下时触发事件 * keydown - > keypress - > keyup * @param target 目标 * @param handler 事件处理函数 * @param option 配置 * @example * // 监听a.xx元素的按键按下 * DOMUtils.keypress(document.querySelector("a.xx"),()=>{ * console.log("按键按下"); * }) * DOMUtils.keypress("a.xx",()=>{ * console.log("按键按下"); * }) */ keypress( target: HTMLElement | Window | typeof globalThis | string, handler: (event: PopsDOMUtils_Event["keypress"]) => void, option?: boolean | AddEventListenerOptions ) { let DOMUtilsContext = this; if (target == null) { return; } if (typeof target === "string") { target = PopsCore.document.querySelector(target) as HTMLElement; } DOMUtilsContext.on(target, "keypress", null, handler, option); } /** * 阻止事件传递 * @param element 要进行处理的元素 * @param eventNameList (可选)要阻止的事件名|列表 * @param capture (可选)是否捕获,默认false * @example * Utils.preventEvent(document.querySelector("a"),"click") * @example * Utils.preventEvent(event); */ preventEvent(event: Event): boolean; /** * 阻止事件传递 * @param element 要进行处理的元素 * @param eventNameList (可选)要阻止的事件名|列表 * @param capture (可选)是否捕获,默认false * @example * Utils.preventEvent(document.querySelector("a"),"click") * @example * Utils.preventEvent(event); */ preventEvent( element: HTMLElement, eventNameList?: string | string[], capture?: boolean ): boolean; preventEvent( element: HTMLElement | Event, eventNameList: string | string[] = [], capture?: boolean ): boolean | undefined { function stopEvent(event: Event) { /* 阻止事件的默认行为发生。例如,当点击一个链接时,浏览器会默认打开链接的URL */ event?.preventDefault(); /* 停止事件的传播,阻止它继续向更上层的元素冒泡,事件将不会再传播给其他的元素 */ event?.stopPropagation(); /* 阻止事件传播,并且还能阻止元素上的其他事件处理程序被触发 */ event?.stopImmediatePropagation(); return false; } if (arguments.length === 1) { /* 直接阻止事件 */ return stopEvent(arguments[0]); } else { /* 添加对应的事件来阻止触发 */ if (typeof eventNameList === "string") { eventNameList = [eventNameList]; } eventNameList.forEach((eventName) => { (element as HTMLElement).addEventListener(eventName, stopEvent, { capture: Boolean(capture), }); }); } } /** * 选择器,可使用以下的额外语法 * * + :contains([text]) 作用: 找到包含指定文本内容的指定元素 * + :empty 作用:找到既没有文本内容也没有子元素的指定元素 * + :regexp([text]) 作用: 找到符合正则表达式的内容的指定元素 * @param selector 选择器 * @example * DOMUtils.selector("div:contains('测试')") * > div.xxx * @example * DOMUtils.selector("div:empty") * > div.xxx * @example * DOMUtils.selector("div:regexp('^xxxx$')") * > div.xxx */ selector<K extends keyof HTMLElementTagNameMap>( selector: K ): HTMLElementTagNameMap[K] | undefined; selector<E extends Element = Element>(selector: string): E | undefined; selector<E extends Element = Element>(selector: string) { return this.selectorAll<E>(selector)[0]; } /** * 选择器,可使用以下的额外语法 * * + :contains([text]) 作用: 找到包含指定文本内容的指定元素 * + :empty 作用:找到既没有文本内容也没有子元素的指定元素 * + :regexp([text]) 作用: 找到符合正则表达式的内容的指定元素 * @param selector 选择器 * @example * DOMUtils.selectorAll("div:contains('测试')") * > [div.xxx] * @example * DOMUtils.selectorAll("div:empty") * > [div.xxx] * @example * DOMUtils.selectorAll("div:regexp('^xxxx$')") * > [div.xxx] * @example * DOMUtils.selectorAll("div:regexp(/^xxx/ig)") * > [div.xxx] */ selectorAll<K extends keyof HTMLElementTagNameMap>( selector: K ): HTMLElementTagNameMap[K][]; selectorAll<E extends Element = Element>(selector: string): E[]; selectorAll<E extends Element = Element>(selector: string) { selector = selector.trim(); if (selector.match(/[^\s]{1}:empty$/gi)) { // empty 语法 selector = selector.replace(/:empty$/gi, ""); return Array.from(PopsCore.document.querySelectorAll<E>(selector)).filter( ($ele) => { return $ele?.innerHTML?.trim() === ""; } ); } else if ( selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i) ) { // contains 语法 let textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i); let text = textMatch![2]; selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, ""); return Array.from(PopsCore.document.querySelectorAll<E>(selector)).filter( ($ele) => { // @ts-ignore return ($ele?.textContent || $ele?.innerText)?.includes(text); } ); } else if ( selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) || selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i) ) { // regexp 语法 let textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i); let pattern = textMatch![2]; let flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i); let flags = ""; if (flagMatch) { pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, ""); flags = flagMatch[3]; } let regexp = new RegExp(pattern, flags); selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, ""); return Array.from(PopsCore.document.querySelectorAll<E>(selector)).filter( ($ele) => { // @ts-ignore return Boolean(($ele?.textContent || $ele?.innerText)?.match(regexp)); } ); } else { // 普通语法 return Array.from(PopsCore.document.querySelectorAll<E>(selector)); } } /** * 匹配元素,可使用以下的额外语法 * * + :contains([text]) 作用: 找到包含指定文本内容的指定元素 * + :empty 作用:找到既没有文本内容也没有子元素的指定元素 * + :regexp([text]) 作用: 找到符合正则表达式的内容的指定元素 * @param $el 元素 * @param selector 选择器 * @example * DOMUtils.matches("div:contains('测试')") * > true * @example * DOMUtils.matches("div:empty") * > true * @example * DOMUtils.matches("div:regexp('^xxxx$')") * > true * @example * DOMUtils.matches("div:regexp(/^xxx/ig)") * > false */ matches( $el: HTMLElement | Element | null | undefined, selector: string ): boolean { selector = selector.trim(); if ($el == null) { return false; } if (selector.match(/[^\s]{1}:empty$/gi)) { // empty 语法 selector = selector.replace(/:empty$/gi, ""); return $el.matches(selector) && $el?.innerHTML?.trim() === ""; } else if ( selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i) ) { // contains 语法 let textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i); let text = textMatch![2]; selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, ""); // @ts-ignore let content = $el?.textContent || $el?.innerText; if (typeof content !== "string") { content = ""; } return $el.matches(selector) && content?.includes(text); } else if ( selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) || selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i) ) { // regexp 语法 let textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i); let pattern = textMatch![2]; let flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i); let flags = ""; if (flagMatch) { pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, ""); flags = flagMatch[3]; } let regexp = new RegExp(pattern, flags); selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, ""); // @ts-ignore let content = $el?.textContent || $el?.innerText; if (typeof content !== "string") { content = ""; } return $el.matches(selector) && Boolean(content?.match(regexp)); } else { // 普通语法 return $el.matches(selector); } } /** * 根据选择器获取上层元素,可使用以下的额外语法 * * + :contains([text]) 作用: 找到包含指定文本内容的指定元素 * + :empty 作用:找到既没有文本内容也没有子元素的指定元素 * + :regexp([text]) 作用: 找到符合正则表达式的内容的指定元素 * @param $el 元素 * @param selector 选择器 * @example * DOMUtils.closest("div:contains('测试')") * > div.xxx * @example * DOMUtils.closest("div:empty") * > div.xxx * @example * DOMUtils.closest("div:regexp('^xxxx$')") * > div.xxxx * @example * DOMUtils.closest("div:regexp(/^xxx/ig)") * > null */ closest<K extends keyof HTMLElementTagNameMap>( $el: HTMLElement | Element, selector: string ): HTMLElementTagNameMap[K] | null; closest<E extends Element = Element>( $el: HTMLElement | Element, selector: string ): E | null; closest<E extends Element = Element>( $el: HTMLElement | Element, selector: string ): E | null { selector = selector.trim(); if (selector.match(/[^\s]{1}:empty$/gi)) { // empty 语法 selector = selector.replace(/:empty$/gi, ""); let $closest = $el?.closest<E>(selector); if ($closest && $closest?.innerHTML?.trim() === "") { return $closest; } return null; } else if ( selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i) ) { // contains 语法 let textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i); let text = textMatch![2]; selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, ""); let $closest = $el?.closest<E>(selector); if ($closest) { // @ts-ignore let content = $el?.textContent || $el?.innerText; if (typeof content === "string" && content.includes(text)) { return $closest; } } return null; } else if ( selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) || selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i) ) { // regexp 语法 let textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i); let pattern = textMatch![2]; let flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i); let flags = ""; if (flagMatch) { pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, ""); flags = flagMatch[3]; } let regexp = new RegExp(pattern, flags); selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, ""); let $closest = $el?.closest<E>(selector); if ($closest) { // @ts-ignore let content = $el?.textContent || $el?.innerText; if (typeof content === "string" && content.match(regexp)) { return $closest; } } return null; } else { // 普通语法 let $closest = $el?.closest<E>(selector); return $closest; } } } class PopsDOMUtils extends PopsDOMUtilsEvent { /** 获取 animationend 在各个浏览器的兼容名 */ getAnimationEndNameList() { return [ "webkitAnimationEnd", "mozAnimationEnd", "MSAnimationEnd", "oanimationend", "animationend", ]; } /** 获取 transitionend 在各个浏览器的兼容名 */ getTransitionEndNameList() { return [ "webkitTransitionEnd", "mozTransitionEnd", "MSTransitionEnd", "otransitionend", "transitionend", ]; } /** * 实现jQuery中的$().offset(); * @param element * @param calcScroll 计算滚动距离 */ offset(element: HTMLElement, calcScroll: boolean = true) { let rect = element.getBoundingClientRect(); let win = element.ownerDocument.defaultView; let resultRect = new DOMRect( calcScroll ? parseFloat((rect.left + (win?.pageXOffset || 0)).toString()) : rect.left, calcScroll ? parseFloat((rect.top + (win?.pageYOffset || 0)).toString()) : rect.top, rect.width, rect.height ); return resultRect; } /** * 获取元素的宽度 * @param element 要获取宽度的元素 * @param isShow 是否已进行isShow,避免爆堆栈 * @param parent 用于判断是否已显示的父元素载体 * @returns 元素的宽度,单位为像素 * @example * // 获取元素a.xx的宽度 * DOMUtils.width(document.querySelector("a.xx")) * DOMUtils.width("a.xx") * > 100 * // 获取window的宽度 * DOMUtils.width(window) * > 400 * @example * // 设置元素a.xx的宽度为200 * DOMUtils.width(document.querySelector("a.xx"),200) * DOMUtils.width("a.xx",200) */ width( element: HTMLElement | string | Window | Document | typeof globalThis, isShow?: boolean, parent?: HTMLElement | ShadowRoot ): number; width( element: HTMLElement | string | Window | Document | typeof globalThis, isShow: boolean = false, parent?: HTMLElement | ShadowRoot ) { let DOMUtilsContext = this; if (typeof element === "string") { element = PopsCore.document.querySelector(element) as HTMLElement; } if (element == null) { return; } if (popsUtils.isWin(element)) { return PopsCore.window.document.documentElement.clientWidth; } if ((element as HTMLElement).nodeType === 9) { /* Document文档节点 */ element = element as Document; return Math.max( element.body.scrollWidth, element.documentElement.scrollWidth, element.body.offsetWidth, element.documentElement.offsetWidth, element.documentElement.clientWidth ); } if (isShow || (!isShow && popsDOMUtils.isShow(element as HTMLElement))) { /* 已显示 */ /* 不从style中获取对应的宽度,因为可能使用了class定义了width !important */ element = element as HTMLElement; /* 如果element.style.width为空 则从css里面获取是否定义了width信息如果定义了 则读取css里面定义的宽度width */ if ( parseFloat(popsDOMUtils.getStyleValue(element, "width").toString()) > 0 ) { return parseFloat( popsDOMUtils.getStyleValue(element, "width").toString() ); } /* 如果从css里获取到的值不是大于0 可能是auto 则通过offsetWidth来进行计算 */ if (element.offsetWidth > 0) { let borderLeftWidth = popsDOMUtils.getStyleValue( element, "borderLeftWidth" ); let borderRightWidth = popsDOMUtils.getStyleValue( element, "borderRightWidth" ); let paddingLeft = popsDOMUtils.getStyleValue(element, "paddingLeft"); let paddingRight = popsDOMUtils.getStyleValue(element, "paddingRight"); let backHeight = parseFloat(element.offsetWidth.toString()) - parseFloat(borderLeftWidth.toString()) - parseFloat(borderRightWidth.toString()) - parseFloat(paddingLeft.toString()) - parseFloat(paddingRight.toString()); return parseFloat(backHeight.toString()); } return 0; } else { /* 未显示 */ element = element as HTMLElement; let { cloneNode, recovery } = popsDOMUtils.showElement(element, parent); let width = DOMUtilsContext.width(cloneNode, true, parent); recovery(); return width; } } /** * 获取元素的高度 * @param element 要获取高度的元素 * @param isShow 是否已进行isShow,避免爆堆栈 * @param parent 用于判断是否已显示的父元素载体 * @returns 元素的高度,单位为像素 * @example * // 获取元素a.xx的高度 * DOMUtils.height(document.querySelector("a.xx")) * DOMUtils.height("a.xx") * > 100 * // 获取window的高度 * DOMUtils.height(window) * > 700 * @example * // 设置元素a.xx的高度为200 * DOMUtils.height(document.querySelector("a.xx"),200) * DOMUtils.height("a.xx",200) */ height( element: HTMLElement | string | Window | Document | typeof globalThis, isShow?: boolean, parent?: HTMLElement | ShadowRoot ): number; height( element: HTMLElement | string | Window | Document | typeof globalThis, isShow: boolean = false, parent?: HTMLElement | ShadowRoot ) { let DOMUtilsContext = this; if (popsUtils.isWin(element)) { return PopsCore.window.document.documentElement.clientHeight; } if (typeof element === "string") { element = PopsCore.document.querySelector(element) as HTMLElement; } if (element == null) { return; } if ((element as Document).nodeType === 9) { element = element as Document; /* Document文档节点 */ return Math.max( element.body.scrollHeight, element.documentElement.scrollHeight, element.body.offsetHeight, element.documentElement.offsetHeight, element.documentElement.clientHeight ); } if (isShow || (!isShow && popsDOMUtils.isShow(element as HTMLElement))) { element = element as HTMLElement; /* 已显示 */ /* 从style中获取对应的高度,因为可能使用了class定义了width !important */ /* 如果element.style.height为空 则从css里面获取是否定义了height信息如果定义了 则读取css里面定义的高度height */ if ( parseFloat(popsDOMUtils.getStyleValue(element, "height").toString()) > 0 ) { return parseFloat( popsDOMUtils.getStyleValue(element, "height").toString() ); } /* 如果从css里获取到的值不是大于0 可能是auto 则通过offsetHeight来进行计算 */ if (element.offsetHeight > 0) { let borderTopWidth = popsDOMUtils.getStyleValue( element, "borderTopWidth" ); let borderBottomWidth = popsDOMUtils.getStyleValue( element, "borderBottomWidth" ); let paddingTop = popsDOMUtils.getStyleValue(element, "paddingTop"); let paddingBottom = popsDOMUtils.getStyleValue( element, "paddingBottom" ); let backHeight = parseFloat(element.offsetHeight.toString()) - parseFloat(borderTopWidth.toString()) - parseFloat(borderBottomWidth.toString()) - parseFloat(paddingTop.toString()) - parseFloat(paddingBottom.toString()); return parseFloat(backHeight.toString()); } return 0; } else { /* 未显示 */ element = element as HTMLElement; let { cloneNode, recovery } = popsDOMUtils.showElement(element, parent); let height = DOMUtilsContext.height(cloneNode, true, parent); recovery(); return height; } } /** * 获取元素的外部宽度(包括边框和外边距) * @param element 要获取外部宽度的元素 * @param 是否已进行isShow,避免爆堆栈 * @param parent 用于判断是否已显示的父元素载体 * @returns 元素的外部宽度,单位为像素 * @example * // 获取元素a.xx的外部宽度 * DOMUtils.outerWidth(document.querySelector("a.xx")) * DOMUtils.outerWidth("a.xx") * > 100 * // 获取window的外部宽度 * DOMUtils.outerWidth(window) * > 400 */ outerWidth( element: HTMLElement | string | Window | Document, isShow?: boolean, parent?: HTMLElement | ShadowRoot ): number; outerWidth( element: HTMLElement | string | Window | Document, isShow: boolean = false, parent?: HTMLElement | ShadowRoot ) { let DOMUtilsContext = this; if (popsUtils.isWin(element)) { return PopsCore.window.innerWidth; } if (typeof element === "string") { element = PopsCore.document.querySelector(element) as HTMLElement; } if (element == null) { return; } element = element as HTMLElement; if (isShow || (!isShow && popsDOMUtils.isShow(element))) { let style = getComputedStyle(element, null); let marginLeft = popsDOMUtils.getStyleValue(style, "marginLeft"); let marginRight = popsDOMUtils.getStyleValue(style, "marginRight"); return element.offsetWidth + marginLeft + marginRight; } else { let { cloneNode, recovery } = popsDOMUtils.showElement(element, parent); let outerWidth = DOMUtilsContext.outerWidth(cloneNode, true, parent); recovery(); return outerWidth; } } /** * 获取元素的外部高度(包括边框和外边距) * @param element 要获取外部高度的元素 * @param isShow 是否已进行isShow,避免爆堆栈 * @param parent 用于判断是否已显示的父元素载体 * @returns 元素的外部高度,单位为像素 * @example * // 获取元素a.xx的外部高度 * DOMUtils.outerHeight(document.querySelector("a.xx")) * DOMUtils.outerHeight("a.xx") * > 100 * // 获取window的外部高度 * DOMUtils.outerHeight(window) * > 700