I have the following (simplified) markup code:
<div id="container">
<div data-value="1">
<span>Click me 1</span>
</div>
<div data-value="2">
<span>Click me 2</span>
</div>
</div>
<div id="messages"></div>
I want to take advantage of bubbling by attaching an event listener only on the #container
, and get the clicked children's data-value
.
document.getElementById('container').addEventListener('click', function(e){
document.getElementById('messages').innerHTML = 'you clicked ' + e.target.dataset.value;
}, false);
Everything works fine if the clicked area is the div
area (in red in the fiddle). How can I get the data-value
also when the click comes from a children of the div (eg click on a blue span) with the data value without changing the event listener?
Here's the fiddle: http://jsfiddle.net/hgLagy31/1/
e.target
is the element the user clicked, it is showing undefined
since the <span>
does not have a data-value
attribute. You could go up the tree and find the nearest ancestor that does contain a data-value
.
document.getElementById('container').addEventListener('click', function(e) { // Find nearest ancestor with data-value defined var node = e.target; while (!node.dataset.value) { node = node.parentNode; } document.getElementById('messages').innerHTML = 'you clicked ' + node.dataset.value; }, false);
#container > div { background: red; } #container > div > span { background: blue; color: white; }
<div id="container"> <div data-value="1"> <span>Click me 1</span> </div> <div data-value="2"> <span>Click me 2</span> </div> </div> <div id="messages"></div>
To make this more robust, you could check whether node
is undefined
before continuing in the while
loop and exit if so:
while (!node.dataset || !node.dataset.value) {
node = node.parentNode;
if (!node) {
document.getElementById('messages').innerHTML =
'Could not find data-value';
return;
}
}
document.getElementById('container').addEventListener('click', function(e) { // Find nearest ancestor with data-value defined var node = e.target; while (!node.dataset || !node.dataset.value) { node = node.parentNode; if (!node) { document.getElementById('messages').innerHTML = 'Could not find data-value'; return; } } document.getElementById('messages').innerHTML = 'you clicked ' + node.dataset.value; }, false);
#container > div { background: red; } #container > div > span { background: blue; color: white; }
<div id="container"> <div data-value="1"> <span>Click me 1</span> </div> <div data-value="2"> <span>Click me 2</span> </div> <div> <span>Click me 3</span> </div> </div> <div id="messages"></div>
As you asked for a solution that doesn't involve changing the event listener...
#container > div > span {
background: blue;
color: white;
pointer-events: none;
}
This will let the click event bubble straight down to the container.
As others have mentioned get the target element and then traverse through the .parentNode
Not all browsers support target/toElement, so I use the following polyfill when getting the target element:
var target = e.toElement || e.relatedTarget || e.target || function () { throw "Failed to attach an event target!"; }
Where e is the event
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.