Search code examples
javascriptdom-eventsevent-bubbling

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 (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/


Solution

  • 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>