In the snippet below, if you:
- Click down in the red
<div>
and hold the click down - Then move the cursor to the blue
<body>
(without releasing yet) - Then release the click when you are in the blue
<body>
Then e.target
counts the click as a click on the <body>
even though the click actually happened to the <div>
element
My scenario is a side menu which should close when the body is clicked, however, I do NOT want the menu to close if the click originated from the menu but was released over the body like what is happening in my snippet. Is this possible to do?
PS: Just to clarify, I am just trying to understand this behavior and how you could get the click’s start element instead of whatever it is that is going on right now. I am not asking how to build the menu or anything.
I am also noticing that if you start the click in the <body>
and then release it over the <div>
, it still counts as a click to the <body>
. This is the opposite effect of what I demonstrated above which is confusing me even further. Is this because it always chooses the greater ancestor of the start and release elements?? I can`t seem to get any results about this when Googling.
PS: I am using google chrome in case this behavior is browser specific.
document.body.addEventListener("click", (e) => {
console.log(e.target);
});
body {
min-height: 10rem;
background: turquoise;
}
.div {
height: 5rem;
display: flex;
align-items: center;
justify-content: center;
background: salmon;
}
<div class="div">Click Down Here</div>
Release Here
2
Answers
Most (not all) events "bubble" up the DOM tree. They fire on the original target element, and then on the chain of parent elements up to the top of the DOM. This is an extremely useful feature, as it allows events to be handled by global top-level handlers without the need to attach handlers all over the place in the DOM.
On the
event
element that’s passed to an event handler, the "target" property will give you the original element involved in the event (the "click" or whatever). The "currentTarget" property will give you the element that is currently called, one of the parents of the target. You can use the.matches()
API in a top-level handler to decide if and how to handle an event from the actual target:If you don’t want to handle an event on a parent element, you can check if the "target" and "currentTarget" elements are different.
You can use the
event.stopPropagation()
method to tell the browser to stop bubbling an event up the tree, orevent.stopImmediatePropagation()
to stop calling handlers for the event entirely.edit — for your case of the side menu, you can check if the target matches an element that’s a descendant of the menu container:
You could try setting up three event listeners. First, listen mousedown and mouseup events and save their target element. Click event will fire only after these two events are completed. So, in the click event you could compare your saved variables and figure out whether both mousedown and mouseup took place on the same element.