If you google for “how to detect clicks outside of an element,” you’ll find that
the top result on stackoverflow suggests binding a click handler on the
html element that should perform the desired action, and another click handler
on the element itself that calls
This works because of event bubbling, where clicking on an element will call
its click handlers, and then its parent’s click handlers, and then its
grandparent’s click handlers, and so on until the root (
html) element’s click
handlers are called, or until
event.stopPropagation() is called in a click
handler on the way up, whichever occurs first. So, the idea is that clicking
anywhere outside of the element will eventually call the click handler on
html, and clicking anywhere inside of the element will eventually run into an
The problem with this approach is that if other elements outside of
also have click handlers that call
html click handler
will never be called, and this is just a straight up bug.
A more robust way to detect clicks outside of an element is to register a click
document for the event capturing phase, and tell the handler to
perform an action only if the target (clicked) element is not a child of the
element for which you want to detect clicks outside.
The event capturing phase comes before the event bubbling phase and traverses the DOM in reverse order. We start at the root element and go down the DOM hierarchy until the target element is reached, triggering the click handlers (that were specified to be triggered during the event capturing phase) on each element on the way. This blog post by Sam Stephenson has some of the best depictions of event bubbling and event capturing.
The third argument to
addEventListener specifies whether the event handler
should be called during the event capturing phase. The guard conditional checks
#container is an ancestor of the clicked element, and do nothing if it
is. If it isn’t, then it means something was clicked outside of
the code will execute.