Skip to content

Instantly share code, notes, and snippets.

@YurkaninRyan
Created July 10, 2019 12:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save YurkaninRyan/ecdbc4098da0333387fac954f724bb31 to your computer and use it in GitHub Desktop.
Save YurkaninRyan/ecdbc4098da0333387fac954f724bb31 to your computer and use it in GitHub Desktop.
useClickOutside
import React from "react";
export default function useClickOutside(onClickOutside, exempt = []) {
const container = React.useRef(null);
const mouseDownTargetIsOutside = React.useRef(false);
React.useEffect(() => {
/* if the click event doesnt start outside of the element then we want to ignore it */
/* imagine if someone clicked while swiping the cursor, if it started inside, then they probably */
/* wouldn't expect mouseup to fire like it was outside */
function onMouseDown(event) {
/* If the target is inside, then don't fire click outside handler */
if (container.current && container.current.contains(event.target)) {
mouseDownTargetIsOutside.current = false;
return;
}
mouseDownTargetIsOutside.current = true;
}
function onMouseUp(event) {
/* If both targets weren't outside then we can bail early */
const mouseUpTargetIsOutside =
!container.current || !container.current.contains(event.target);
if (!mouseDownTargetIsOutside.current || !mouseUpTargetIsOutside) {
return;
}
/* exempt is an array of strings (css selectors) or html elements */
/* Here we figure out if the target matches either, or is nested inside an exempt element */
const targetIsExempt = exempt.some(selectorOrElement => {
if (typeof selectorOrElement === "string") {
return Boolean(event.target.closest(selectorOrElement));
} else if (
selectorOrElement instanceof HTMLElement ||
selectorOrElement.current instanceof HTMLElement
) {
const element = selectorOrElement.current || selectorOrElement;
return event.target === element || element.contains(event.target);
}
return false;
});
if (targetIsExempt) {
return;
}
onClickOutside(event, container);
}
window.addEventListener("mousedown", onMouseDown, true);
window.addEventListener("mouseup", onMouseUp, true);
return () => {
window.removeEventListener("mousedown", onMouseDown, true);
window.removeEventListener("mouseup", onMouseUp, true);
};
});
return container;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment