Comments (12)
The are no plans for this, the beauty of hooks IMHO lies in their composability and I prefer this over configurability and too many features inside "the box" 😉
You can easily build this on top of this hook (which you kinda already have done 🚀). You could go even further and encapsulate this behaviour in a reusable fashion:
// myUseOnClickOutside.js
import useOnClickOutside from 'use-onclickoutside'
const hasIgnoredClass = (element, ignoredClass) =>
(element.correspondingElement
? element.correspondingElement
: element
).classList.contains(ignoredClass)
const isInIgnoredElement = (element, ignoredClass) => {
do {
if (hasIgnoredClass(element, ignoredClass)) {
return true
}
} while ((element = element.parentElement))
return false
}
export default (ref, handler, ignoredClass = 'ignore-onClickOutside') =>
useOnClickOutside(ref, event => {
if (isInIgnoredElement(event.target, ignoredClass)) {
return
}
handler(event)
})
I could however consider exporting this in "addon" form, so everyone could compose their own logic (if needed) - but still benefit from the centralized addons. Not sure what API exactly that could have though.
from use-onclickoutside.
If you want to not trigger it when clicking inside some element, you can also do it this way:
const [open, setOpen] = React.useState(false)
const buttonRef = React.useRef(null)
const clickOutsideRef = React.useRef(null)
useOnClickOutside(ref, event => {
if (!buttonRef.current.contains(event.target)) {
setOpen(false)
}
})
return (<>
<button ref={buttonRef} onClick={() => setOpen(!open)}>Click to toggle</button>
{ open && <div ref={clickOutsideRef}>…</div> }
</>)
from use-onclickoutside.
The are no plans for this, the beauty of hooks IMHO lies in their composability and I prefer this over configurability and too many features inside "the box" 😉
You can easily build this on top of this hook (which you kinda already have done 🚀). You could go even further and encapsulate this behaviour in a reusable fashion:
// myUseOnClickOutside.js import useOnClickOutside from 'use-onclickoutside' const hasIgnoredClass = (element, ignoredClass) => (element.correspondingElement ? element.correspondingElement : element ).classList.contains(ignoredClass) const isInIgnoredElement = (element, ignoredClass) => { do { if (hasIgnoredClass(element, ignoredClass)) { return true } } while ((element = element.parentElement)) return false } export default (ref, handler, ignoredClass = 'ignore-onClickOutside') => useOnClickOutside(ref, event => { if (isInIgnoredElement(event.target, ignoredClass)) { return } handler(event) })I could however consider exporting this in "addon" form, so everyone could compose their own logic (if needed) - but still benefit from the centralized addons. Not sure what API exactly that could have though.
@Andarist Thanks for the snippet 😄
Although for some odd reason in rare occasions, event.target was null, causing hasIgnoredClass to error on the first call.
Here's a fix if anyones interested, with added recursion because reasons 💫
// myUseOnClickOutside.js
import useOnClickOutside from 'use-onclickoutside';
const hasClass = (element, className) => (
element && (
element.correspondingElement ? element.correspondingElement : element
).classList.contains(className)
);
// Returns true if the given element, or any parentElement has the ignoredClass class
const isInIgnoredElement = (element, ignoredClass) => {
if (!element) {
return false;
}
if (hasClass(element, ignoredClass)) {
return true;
}
return isInIgnoredElement(element.parentElement, ignoredClass);
};
export default (ref, handler, ignoredClass = 'ignore-onClickOutside') => (
useOnClickOutside(ref, (event) => {
if (isInIgnoredElement(event.target, ignoredClass)) {
return;
}
handler(event);
})
);
from use-onclickoutside.
Thanks for the in-depth answer! I'll definitely use your snippet if I find myself having to reuse the ignored class logic all over the place.
from use-onclickoutside.
You may also use [...document.querySelectorAll(className)].some(x => evt.target.contains(x))
to avoid to traverse the DOM manually.
from use-onclickoutside.
Yeah, sure - but u do a lot more of traversing by querying whole DOM first and then querying received subtrees. Whereas when doing manual traversal you check only a single node path
from use-onclickoutside.
I think querySelector
and querySelectorAll
are very optimised, but I guess one would need to benchmark to be sure.
from use-onclickoutside.
This is what I would probably do in "trigger+tooltip~" situation 👍
from use-onclickoutside.
@Andarist what about multiple refs as parameter, do you still prefer doing multiple hooks calls?
from use-onclickoutside.
@Andarist just implemented a dropdown and not supporting multiple refs makes it a bit more complicated.
const buttonRef = useRef<HTMLDivElement | null>(null);
const dropdownRef = useRef<HTMLDivElement | null>(null);
useOnClickOutside(buttonRef, event => {
const isDropdownClick =
dropdownRef.current && dropdownRef.current.contains(event.target as Node);
if (!isDropdownClick) {
setOpened(false);
}
});
I'd rather have this instead:
const buttonRef = useRef<HTMLDivElement | null>(null);
const dropdownRef = useRef<HTMLDivElement | null>(null);
useOnClickOutside([buttonRef,dropdownRef], event => {
setOpened(false);
});
from use-onclickoutside.
how would it work? would it check against if the click originated outside of ANY of given refs or outside of ALL of them?
from use-onclickoutside.
I would say any of them.
If you want "contained in all of them", you can pass the most "specific" ref only to get that behavior, or it will always be false if refs are not nested. Can't find an usecase where this would be useful
from use-onclickoutside.
Related Issues (14)
- Logging just to ask some questions HOT 2
- Doesn't handle portals HOT 4
- How this package works so it uses event listeners effectively? HOT 2
- How to test a component using this hook? HOT 1
- Add `capture` option for event handler? HOT 5
- [Question] Why does it use "mousedown" and not "click"? HOT 10
- Clashes with mouse down events creating the element HOT 2
- Account for multiple refs? HOT 9
- Don't trigger when scrolling down on mobile? HOT 1
- Add unit tests
- Feature request: adding/excluding Events HOT 1
- Update peerDependency to react 18 HOT 2
- [Feature Requrest] Option to use `window.addEventListener` rather than `document.addEventListener` HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from use-onclickoutside.