Reputation: 8169
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 (e.g. click on a blue span) with the data value without changing the event listener?
Here's the fiddle: http://jsfiddle.net/hgLagy31/1/
Upvotes: 5
Views: 101
Reputation: 50189
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>
Upvotes: 4
Reputation: 1033
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
Upvotes: 0
Reputation: 1816
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.
Upvotes: 0