Skip to content

Instantly share code, notes, and snippets.

@zeusdeux
Last active May 26, 2023 15:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeusdeux/4913e2c56da784f5a904c796c43b3229 to your computer and use it in GitHub Desktop.
Save zeusdeux/4913e2c56da784f5a904c796c43b3229 to your computer and use it in GitHub Desktop.
Non-💩 and dirt simple click-outside directive for Vue 3
import type { Directive } from "vue";
type ClickOutsideHandler = ($event: MouseEvent | TouchEvent) => void;
type ClickOutsideHandlerMap = Map<HTMLElement, ClickOutsideHandler> & {
initialized?: boolean;
};
const clickOutsideHandlersSymbol: symbol = Symbol.for("v-click-outside-handlers");
// @ts-ignore
if (typeof window === "object") {
// @ts-ignore
window[clickOutsideHandlersSymbol] =
// @ts-ignore
window[clickOutsideHandlersSymbol] ?? new Map<HTMLElement, ClickOutsideHandler>();
// @ts-ignore
const globalClickOutsideHandlersMap: ClickOutsideHandlerMap = window[clickOutsideHandlersSymbol];
if (!globalClickOutsideHandlersMap.initialized) {
window.document.addEventListener(
"click",
($event) => {
globalClickOutsideHandlersMap.forEach((handler) => {
handler($event);
});
},
{ capture: true, passive: true },
);
window.document.addEventListener(
"touchstart",
($event) => {
globalClickOutsideHandlersMap.forEach((handler) => {
handler($event);
});
},
{ capture: true, passive: true },
);
globalClickOutsideHandlersMap.initialized = true;
}
}
const registerClickOutsideHandler = ($el: HTMLElement, { value: handler }: { value: Function }) => {
const globalClickOutsideHandlersMap: ClickOutsideHandlerMap =
// @ts-ignore
window[clickOutsideHandlersSymbol];
globalClickOutsideHandlersMap.set($el, ($event: MouseEvent | TouchEvent) => {
// @ts-ignore
if ($event.target !== $el && !$el.contains($event.target)) {
handler();
}
});
};
const cleanupClickOutsideHandler = ($el: HTMLElement) => {
const globalClickOutsideHandlersMap: ClickOutsideHandlerMap =
// @ts-ignore
window[clickOutsideHandlersSymbol];
globalClickOutsideHandlersMap.delete($el);
};
const directive: Directive = {
mounted: registerClickOutsideHandler,
updated: registerClickOutsideHandler,
beforeUnmount: cleanupClickOutsideHandler,
};
export default directive;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment