diff --git a/src/addEventListener.ts b/src/addEventListener.ts index dc61c0a..85a30f7 100644 --- a/src/addEventListener.ts +++ b/src/addEventListener.ts @@ -1,10 +1,6 @@ -import { isStr } from './isStr' -import { animationFrameWrapper } from './animationFrameWrapper' -import { findElement } from './findElement' +import { mount } from './mount' export function addEventListener(target: Window | Document | Element | string, eventName: string, callback: (e: any) => void, useCapture?: boolean | AddEventListenerOptions, autoRemove?: boolean): (() => void) { - let isMounted = false - let hasMounted = false let stopped = false let stop: () => void let animationStop: (() => void) @@ -21,29 +17,11 @@ export function addEventListener(target: Window | Document | Element | string, e if (autoRemove) stop() } - update() - window.addEventListener('DOMContentLoaded', () => { - update() - if (stopped) - stop?.() - }) window.onunload = () => stop?.() - if (eventName === 'DOMContentLoaded') - animationStop = animationFrameWrapper(callback, 0, true) - else - animationFrameWrapper(update, 0, true) - function update() { - if (hasMounted) - return - if (isStr(target)) - target = findElement(target) as Element || target - if (!isMounted && isStr(target)) - return isMounted = true - else if (isStr(target)) - throw new Error(`${target} is not a Element`) - const originCall = (target as unknown as any)?.[eventName] + mount(target, (target) => { + const originCall = (target as any)?.[eventName] const eventFunction = (e: Event) => { try { const isRawClick = originCall && originCall.toString() === 'function click() { [native code] }' @@ -59,10 +37,10 @@ export function addEventListener(target: Window | Document | Element | string, e stop = () => (target as Element).removeEventListener(eventName, eventFunction, useCapture) if (stopped) stop?.() - hasMounted = true - } + }) return () => { - if (!stop) return stopped = true + if (!stop) + return stopped = true stop?.() } } diff --git a/src/dragEvent.ts b/src/dragEvent.ts index f9966ac..5d4a54d 100644 --- a/src/dragEvent.ts +++ b/src/dragEvent.ts @@ -1,53 +1,44 @@ import { getDevice } from './getDevice' -import { isStr } from './isStr' import { addEventListener } from './addEventListener' import type { DragEvent } from './types' -import { findElement } from './findElement' +import { mount } from './mount' export function dragEvent(target: HTMLElement | string, options: DragEvent = {}, trigger?: boolean) { - let isMounted = false - let hasMounted = false const { os } = getDevice() const isPhone = os === 'ios' || os === 'android' const stop: (() => void)[] = [] - function update() { - if (hasMounted) - return - if (isStr(target)) - target = findElement(target) || target - if (!isMounted && isStr(target)) - return isMounted = true - else if (isStr(target)) - throw new Error(`${target} is not a HTMLElement`) + let isStopped = false + mount(target, (target) => { let down = false if (isPhone) { - stop.push(addEventListener(target as HTMLElement, 'touchstart', (e: any) => { + stop.push(addEventListener(target, 'touchstart', (e: any) => { options.dragStart && options.dragStart(wrapperE(e)) }, false)) - options.dragMove && stop.push(addEventListener(target as HTMLElement, 'touchmove', (e: any) => { + options.dragMove && stop.push(addEventListener(target, 'touchmove', (e: any) => { if (!trigger || down) options.dragMove?.(wrapperE(e)) }, false)) - options.dragEnd && stop.push(addEventListener(target as HTMLElement, 'touchend', (e: any) => { + options.dragEnd && stop.push(addEventListener(target, 'touchend', (e: any) => { options.dragEnd?.(wrapperE(e)) down = false }, false)) - } + } else { - stop.push(addEventListener(target as HTMLElement, 'mousedown', (e: any) => { + stop.push(addEventListener(target, 'mousedown', (e: any) => { down = true options.dragStart && options.dragStart(e) }, false)) - options.dragMove && stop.push(addEventListener(target as HTMLElement, 'mousemove', (e: any) => { + options.dragMove && stop.push(addEventListener(target, 'mousemove', (e: any) => { if (!trigger || down) options.dragMove?.(e) }, false)) - options.dragEnd && stop.push(addEventListener(target as HTMLElement, 'mouseup', (e: any) => { + options.dragEnd && stop.push(addEventListener(target, 'mouseup', (e: any) => { options.dragEnd?.(e) down = false }, false)) } - hasMounted = true + if (isStopped) + stop.forEach(stop => stop()) function wrapperE(e: any) { const { clientX, clientY, pageX, pageY, screenX, screenY } = e?.changedTouches[0] e.clientX = clientX @@ -58,10 +49,10 @@ export function dragEvent(target: HTMLElement | string, options: DragEvent = {}, e.screenY = screenY return e } - } - update() - addEventListener(document, 'DOMContentLoaded', update) + }) return () => { + if (!stop.length) + return isStopped = true stop.forEach(cb => cb?.()) } } diff --git a/src/insertElement.ts b/src/insertElement.ts index 59868ba..e1fb30e 100644 --- a/src/insertElement.ts +++ b/src/insertElement.ts @@ -1,26 +1,5 @@ -import { addEventListener } from './addEventListener' -import { findElement } from './findElement' -import { isStr } from './isStr' +import { mount } from './mount' export function insertElement(parent: HTMLElement | string, element: HTMLElement | string, target?: HTMLElement | null): void { - let isMounted = false - let hasMounted = false - update() - addEventListener(document, 'DOMContentLoaded', update) - function update() { - if (hasMounted) - return - if (isStr(parent)) - parent = findElement(parent) || parent - if (isStr(element)) - element = findElement(element) || element - if (!isMounted && (isStr(parent) || isStr(element))) - return isMounted = true - if (isStr(parent)) - throw new Error(`${parent} is not a HTMLElement`) - if (isStr(element)) - throw new Error(`${element} is not a HTMLElement`); - (parent as HTMLElement).insertBefore(element as HTMLElement, target === undefined ? (parent as HTMLElement).firstChild : target) - hasMounted = true - } + mount(parent, element, (parent, element) => (parent as HTMLElement).insertBefore(element as HTMLElement, target === undefined ? (parent as HTMLElement).firstChild : target)) } diff --git a/src/mount.ts b/src/mount.ts new file mode 100644 index 0000000..dc7b0ea --- /dev/null +++ b/src/mount.ts @@ -0,0 +1,26 @@ +import { findElement } from './findElement' +import { isStr } from './isStr' + +type MountArgs = [...Array, (...elements: Element[]) => void] +export function mount(...args: MountArgs): void { + const len = args.length + const params = [...args] + const elements = params.slice(0, len - 1) + const callback = params.slice(-1)[0] as unknown as (...elements: Element[]) => void + let isMounted = false + let hasMounted = false + update() + document.addEventListener('DOMContentLoaded', update) + setTimeout(() => document.removeEventListener('DOMContentLoaded', update)) + function update() { + if (hasMounted) + return + elements.forEach((element, index) => isStr(element) && (elements[index] = findElement(element) || element)) + if (!isMounted && elements.some(isStr)) + return isMounted = true + if (elements.some(isStr)) + throw new Error(`${elements.filter(isStr).join(', ')} is not a HTMLElement`) + callback?.(...elements as Element[]) + hasMounted = true + } +} diff --git a/src/useClick.ts b/src/useClick.ts index 8bafa5b..4a2c2bc 100644 --- a/src/useClick.ts +++ b/src/useClick.ts @@ -1,30 +1,14 @@ -import { isStr } from './isStr' import { addEventListener } from './addEventListener' -import { findElement } from './findElement' +import { mount } from './mount' export function useClick(target: string | HTMLElement, callback: () => void) { - let isMounted = false - let hasMounted = false let stop: () => void let stopped = false - - update() - addEventListener(window, 'DOMContentLoaded', update) - - function update() { - if (hasMounted) - return - if (isStr(target)) - target = findElement(target) || target - if (!isMounted && isStr(target)) - return isMounted = true - else if (isStr(target)) - throw new Error(`${target} is not a Element`) - stop = addEventListener(target, 'click', callback) + mount(target, (target) => { + stop = addEventListener(target as Element, 'click', callback) if (stopped) stop?.() - hasMounted = true - } + }) return () => { if (!stop) return stopped = true diff --git a/src/useElementBounding.ts b/src/useElementBounding.ts index 6b3924a..900a4b2 100644 --- a/src/useElementBounding.ts +++ b/src/useElementBounding.ts @@ -1,23 +1,7 @@ import { addEventListener } from './addEventListener' -import { findElement } from './findElement' -import { isStr } from './isStr' - +import { mount } from './mount' export function useElementBounding(element: Element | string, callback: (rect: DOMRect) => void) { - let isMounted = false - let hasMounted = false - update() - addEventListener(document, 'DOMContentLoaded', update) - return addEventListener(window, 'scroll', update) - function update() { - if (hasMounted) - return - if (isStr(element)) - element = findElement(element) || element - if (!isMounted && isStr(element)) - return isMounted = true - if (isStr(element)) - throw new Error(`${element} is not a Element`) - callback?.((element as Element).getBoundingClientRect()) - hasMounted = true - } + mount(element, el => callback?.((element = el).getBoundingClientRect())) + return addEventListener(window, 'scroll', () => callback?.((element as Element).getBoundingClientRect())) } + diff --git a/src/useFocus.ts b/src/useFocus.ts index db5d5df..c753f2d 100644 --- a/src/useFocus.ts +++ b/src/useFocus.ts @@ -1,22 +1,5 @@ import { findElement } from './findElement' -import { isStr } from './isStr' -import { addEventListener } from './addEventListener' - +import { mount } from './mount' export function useFocus(target: string | HTMLElement) { - let isMounted = false - let hasMounted = false - update() - addEventListener(document, 'DOMContentLoaded', update) - function update() { - if (hasMounted) - return - if (isStr(target)) - target = findElement(target) as HTMLElement || target - if (!isMounted && isStr(target)) - return isMounted = true - else if (isStr(target)) - throw new Error(`${target} is not a Element`) - findElement('input', target.parentElement!)?.focus() - hasMounted = true - } + mount(target, target => findElement('input', target.parentElement!)?.focus()) } diff --git a/src/useHover.ts b/src/useHover.ts index 883cd8e..d64dc68 100644 --- a/src/useHover.ts +++ b/src/useHover.ts @@ -1,24 +1,18 @@ -import { findElement } from './findElement' -import { isStr } from './isStr' import { addEventListener } from './addEventListener' +import { mount } from './mount' -export function useHover(target: string | HTMLElement, callback: (isHover: boolean) => void) { - let hasMounted = false - let isMounted = false - update() - addEventListener(document, 'DOMContentLoaded', update) - - function update() { - if (hasMounted) - return - if (isStr(target)) - target = findElement(target) as HTMLElement || target - if (!isMounted && isStr(target)) - return isMounted = true - else if (isStr(target)) - throw new Error(`${target} is not a Element`) - addEventListener(target, 'mouseenter', () => callback(true)) - addEventListener(target, 'mouseleave', () => callback(false)) - hasMounted = true +export function useHover(target: string | HTMLElement, callback: (isHover: boolean) => void): () => void { + let stopped = false + const stop: (() => void)[] = [] + mount(target, (target) => { + stop.push(addEventListener(target, 'mouseenter', () => callback(true))) + stop.push(addEventListener(target, 'mouseleave', () => callback(false))) + if (stopped) + stop.forEach(stop => stop()) + }) + return () => { + if (!stop.length) + return stopped = true + stop.forEach(fn => fn()) } } diff --git a/src/useMutationObserver.ts b/src/useMutationObserver.ts index f2e6556..943c7df 100644 --- a/src/useMutationObserver.ts +++ b/src/useMutationObserver.ts @@ -1,33 +1,17 @@ -import { isStr } from './isStr' -import { addEventListener } from './addEventListener' import type { MutationObserverInit } from './types' -import { findElement } from './findElement' +import { mount } from './mount' export function useMutationObserver(element: Element | string | ParentNode | null, callback: MutationCallback, options: MutationObserverInit = {}) { - if (!element) + if (!element) return - let isMounted = false - let hasMounted = false let stopped = false let stop: () => void - - update() - addEventListener(document, 'DOMContentLoaded', update) - function update() { - if (hasMounted) - return - if (isStr(element)) - element = findElement(element) || element - if (!isMounted && isStr(element)) - return isMounted = true - else if (isStr(element)) - throw new Error(`${element} is not a Element`) + mount(element, (element) => { const mutationObserver = new MutationObserver(callback) mutationObserver.observe(element as Element, options) stop = () => mutationObserver.disconnect() - hasMounted = true if (stopped) stop() - } + }) return () => { if (!stop) return stopped = true diff --git a/src/useVideo.ts b/src/useVideo.ts index 8e7a365..49cb1c2 100644 --- a/src/useVideo.ts +++ b/src/useVideo.ts @@ -1,7 +1,5 @@ import { createElement } from './createElement' -import { isStr } from './isStr' import { addEventListener } from './addEventListener' -import { findElement } from './findElement' interface Sources { src: string @@ -16,11 +14,9 @@ interface VideoOptions { style?: string } export function useVideo(sources: Sources[] = [], videoOptions: VideoOptions) { - let isMounted = false - let hasMounted = false const video = createElement('video') as HTMLVideoElement const { controls = true, width, height, className, style } = videoOptions - let container = videoOptions.container + const container = videoOptions.container video.controls = controls if (width) video.width = width! @@ -30,9 +26,9 @@ export function useVideo(sources: Sources[] = [], videoOptions: VideoOptions) { video.className = className if (style) video.style.cssText = style - update() addEventListener(document, 'DOMContentLoaded', update) addEventListener(video, 'timeupdate', () => (video.currentTime >= video.duration) && playReset()) + return { play() { if (video.paused) @@ -57,14 +53,6 @@ export function useVideo(sources: Sources[] = [], videoOptions: VideoOptions) { } function update() { - if (hasMounted) - return - if (isStr(container)) - container = findElement(container) || container - if (!isMounted && isStr(container)) - return isMounted = true - if (isStr(container)) - throw new Error(`${container} is not a Element`) const source = createElement('source') sources.forEach(({ src, type }) => { const _source = source.cloneNode() as HTMLSourceElement @@ -73,6 +61,5 @@ export function useVideo(sources: Sources[] = [], videoOptions: VideoOptions) { video.appendChild(_source) }); (container as HTMLElement).appendChild(video) - hasMounted = true } }