JavaScript события и пузыри

У меня есть следующий (упрощенный) код разметки:

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

Я хочу воспользоваться всплеском, подключив прослушиватель событий только на #container, и получить кликнул детский data-value,

document.getElementById('container').addEventListener('click', function(e){
    document.getElementById('messages').innerHTML = 'you clicked ' + e.target.dataset.value;
}, false);

Все работает нормально, если область нажатия div площадь (красным на скрипке). Как я могу получить data-value также, когда щелчок происходит от дочерних элементов div (например, щелчок по синему промежутку) со значением данных без изменения прослушивателя событий?

Вот скрипка: http://jsfiddle.net/hgLagy31/1/

3 ответа

Решение

e.target элемент, по которому щелкнул пользователь, показывает undefined так как <span> не имеет data-value приписывать. Вы можете подняться на дерево и найти ближайшего предка, который содержит 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>

Чтобы сделать это более надежным, вы можете проверить, node является undefined прежде чем продолжить в while цикл и выход, если так:

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>

Как уже упоминалось, получить целевой элемент, а затем пройти через .parentNode

Не все браузеры поддерживают target/toElement, поэтому я использую следующий polyfill при получении целевого элемента:

var target = e.toElement || e.relatedTarget || e.target || function () { throw "Failed to attach an event target!"; }

Где е это событие

Как вы просили решение, которое не включает в себя изменение слушателя событий...

#container > div > span {
    background: blue;
    color: white;
    pointer-events: none;
}

Это позволит событию click всплыть прямо в контейнер.

Другие вопросы по тегам