How to Detect a Click Outside an Element
A common pattern used in JavaScript is detecting a click outside an element. This snippet offers you the most efficient ones among the various solutions.
One of the most common patterns used in JavaScript is detecting a click outside an element. You can apply it for closing a non-modal user interface component like a menu or a dropdown when the user clicks outside that element.
There is a variety of solutions to this issue.
Solution 1
One of them is implementing a vanilla JavaScript solution.
The flyout in HTML will look like this:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<div class="flyout" id="flyout-example">
<h5 class="flyout-title">This could be a flyout;</h5>
<div class="flyout-debug" id="flyout-debug"></div>
<div class="flyout-buttons">
<button class="button button-outline" type="button">Cancel</button>
<button class="button" type="button">Ok</button>
</div>
</div>
</body>
</html>So, for detecting a click outside an element, it would be best if you add a listener to the whole document element. Consequently, the traversal loop will go up the DOM from the clicked target element to check if any ancestor of that element belongs to the flyout container.
Using modern ES6+ features, the JavaScript code looks like this:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<div class="flyout" id="flyout-example">
<h5 class="flyout-title">This could be a flyout;</h5>
<div class="flyout-debug" id="flyout-debug"></div>
<div class="flyout-buttons">
<button class="button button-outline" type="button">Cancel</button>
<button class="button" type="button">Ok</button>
</div>
</div>
<script>
document.addEventListener("click", (evt) => {
const flyoutEl = document.getElementById("flyout-example");
let targetEl = evt.target; // clicked element
do {
if(targetEl == flyoutEl) {
// This is a click inside, does nothing, just return.
document.getElementById("flyout-debug").textContent = "Clicked inside!";
return;
}
// Go up the DOM
targetEl = targetEl.parentNode;
} while (targetEl);
// This is a click outside.
document.getElementById("flyout-debug").textContent = "Clicked outside!";
});
</script>
</body>
</html>Notice that this code can run on modern browsers. For older browsers, you need to adapt the code, which will look as follows:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<div class="flyout" id="flyout-example">
<h5 class="flyout-title">This could be a flyout;</h5>
<div class="flyout-debug" id="flyout-debug"></div>
<div class="flyout-buttons">
<button class="button button-outline" type="button">Cancel</button>
<button class="button" type="button">Ok</button>
</div>
</div>
<script>
document.addEventListener("click", function(evt) {
var flyoutEl = document.getElementById('flyout-example'),
targetEl = evt.target; // clicked element
do {
if(targetEl == flyoutEl) {
// This is a click inside, does nothing, just return.
document.getElementById("flyout-debug").textContent = "Clicked inside!";
return;
}
// Go up the DOM
targetEl = targetEl.parentNode;
} while (targetEl);
// This is a click outside.
document.getElementById("flyout-debug").textContent = "Clicked outside!";
});
</script>
</body>
</html>Solution 2
This approach requires the jQuery library. You need to attach a click event to the document body that closes the window, and attach an independent click event to the container that stops propagation to the body of the document.
Here is an example:
$(window).click(function () { //Hide the menus if visible });
$('#menucontainer').click(function (event) {event.stopPropagation();});