简体   繁体   中英

JavaScript events and bubbling

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM