Skip to content

Instantly share code, notes, and snippets.

@sibbng
Created August 27, 2021 21:49
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save sibbng/13e83b1dd1b733317ce0130ef07d4efd to your computer and use it in GitHub Desktop.
Save sibbng/13e83b1dd1b733317ce0130ef07d4efd to your computer and use it in GitHub Desktop.
import { unrefElement, useEventListener } from '@vueuse/core'
const defaultWindow = typeof window !== 'undefined' ? window : undefined
/**
* Listen for clicks outside of an element.
*
* @see https://vueuse.org/onClickOutside
* @param target
* @param handler
* @param options
*/
export function onClickOutside(
target,
handler,
options = {},
) {
const { window = defaultWindow, event = 'pointerdown' } = options
if (!window)
return
const listener = (event) => {
const el = unrefElement(target)
if (!el || el === event.target || eventPath(event).includes(el))
return
handler(event)
}
function eventPath(event) {
const path = (event.composedPath && event.composedPath()) || event.path
if (path != null)
return path
function getParents(node, memo = []) {
const parentNode = node.parentNode
return parentNode
? getParents(parentNode, memo.concat([parentNode]))
: memo
}
return [event.target].concat(getParents(event.target))
}
return useEventListener(window, event, listener, { passive: true })
}
import { MaybeElementRef, unrefElement, useEventListener } from '@vueuse/core'
interface ConfigurableWindow {
/*
* Specify a custom `window` instance, e.g. working with iframes or in testing environments.
*/
window?: Window
}
const defaultWindow = typeof window !== 'undefined' ? window : undefined
export type OnClickOutsideEvents = Pick<WindowEventMap, 'click' | 'mousedown' | 'mouseup' | 'touchstart' | 'touchend' | 'pointerdown' | 'pointerup'>
export interface OnClickOutsideOptions<E extends keyof OnClickOutsideEvents> extends ConfigurableWindow {
event?: E
}
/**
* Listen for clicks outside of an element.
*
* @see https://vueuse.org/onClickOutside
* @param target
* @param handler
* @param options
*/
export function onClickOutside<E extends keyof OnClickOutsideEvents = 'pointerdown'>(
target: MaybeElementRef,
handler: (evt: OnClickOutsideEvents[E]) => void,
options: OnClickOutsideOptions<E> = {},
) {
const { window = defaultWindow, event = 'pointerdown' } = options
if (!window)
return
const listener = (event: OnClickOutsideEvents[E]) => {
const el = unrefElement(target)
if (!el || el === event.target || eventPath(event).includes(el))
return
handler(event)
}
function eventPath(event: OnClickOutsideEvents[E]) {
// @ts-ignore
const path = ((event.composedPath && event.composedPath()) || event.path) as HTMLElement[] | undefined
if (path != null)
return path
function getParents(node: HTMLElement, memo: HTMLElement[] = []): HTMLElement[] {
const parentNode = node.parentNode as HTMLElement | null
return parentNode
? getParents(parentNode, memo.concat([parentNode]))
: memo
}
return [event.target].concat(getParents(event.target as HTMLElement))
}
return useEventListener(window, event, listener, { passive: true })
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment