Created
July 10, 2019 12:48
-
-
Save YurkaninRyan/ecdbc4098da0333387fac954f724bb31 to your computer and use it in GitHub Desktop.
useClickOutside
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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