I've been trying to detect whether :focus-within
class was lost. I tried to detect click-outside of by using addEventListener for 'click', 'auxclick', 'blur', 'mouseup'. But I can't figure out how to detect a click outside of the actual document. For example, click on URL input. How can I solve it?
Asked
Active
Viewed 3,647 times
5

Daniel
- 839
- 10
- 20
-
Could you explain more in details so that everyone can understand the question in an easy way and provide you best solution on it ? – kunal panchal Feb 04 '21 at 14:47
-
3The event you need is `focusout` and the test you want to do when the event occurs is `event.target.matches(':focus-within')`. Actions that don't actually remove focus (like you describe) cannot be listened to. – connexo Feb 04 '21 at 14:59
1 Answers
7
To detect whether a DOM element has lost :focus-within
, you can use the focusout
event like this:
containerElement.addEventListener('focusout', function(e) {
if (e.currentTarget.contains(e.relatedTarget)) {
/* Focus will still be within the container */
} else {
/* Focus will leave the container */
}
});
When focus is lost by the page altogether (visiting an URL, switching tabs, etc.), e.relatedTarget
doesn't exist so the code just works. If you want to ignore when the page loses focus, you can use document.hasFocus()
to check:
containerElement.addEventListener('focusout', function(e) {
/*
If the document has lost focus,
skip the containment check
and keep the element visible.
*/
if (!document.hasFocus()) {
return;
}
if (!e.currentTarget.contains(e.relatedTarget)) {
hideSelf();
}
});
...but then you have to react when the focus does get back to the page, so the full(er) solution looks something like this:
containerElement.addEventListener('focusout', function(e) {
const self = e.currentTarget;
/*
If the document has lost focus,
don't hide the container just yet,
wait until the focus is returned.
*/
if (!document.hasFocus()) {
window.addEventListener('focus', function focusReturn() {
/*
We want the listener to be triggered just once,
so we have it remove itself from the `focus` event.
*/
window.removeEventListener('focus', focusReturn);
/*
Test whether the active element
is within our container.
*/
if (!self.contains(document.activeElement)) {
hideSelf();
}
});
return;
}
if (!self.contains(e.relatedTarget)) {
hideSelf();
}
});

Dan
- 9,912
- 18
- 49
- 70