Слушатель событий Javascript срабатывает дважды при нажатии на текст внутри элемента метки - PullRequest
0 голосов
/ 18 октября 2018

Как сделать так, чтобы функция не запускалась дважды при нажатии на текст внутри метки.

Если я использую event.preventDefault (), тогда основные функции браузера для установки флажка тоже перестанут работать.

const label = document.querySelector('.parent');

label.addEventListener('click', handleLabelClick);

function handleLabelClick(event) {
	console.log('Clicked')
}
<div class="parent">
  <label for="option1">
    <span>Select me</span>
    <input id="option1" type="checkbox">
  </label>
</div>

Ответы [ 2 ]

0 голосов
/ 06 марта 2019

Это стандартное (неудачное) поведение браузера.
Атрибут for назначает <label> на <input>, поэтому при нажатии на элемент <label> браузер будет эмулировать нажатие на элемент <input> сразу послеваш реальный щелчок.
Это позволяет фокусировать текст <input>, переключать радио <input> или переключать флажок <input>.
Но, к сожалению, это также приводит к тому, что оба элементаотправьте нажмите событие .Если вы прослушиваете клики по родительскому элементу, это означает, что вы получите одно «взаимодействие с человеком» дважды.Один раз из <input> и один раз из "элемента, по которому щелкнули".

Для тех, кто интересуется, <input> может быть внутри элемента <label>, вы получите меньше возможностей для стилизации,но затем вы можете нажать между флажком и текстом.

Помещение <span> и <input> в <label> фактически создает хороший тестовый пример.
Попробуйте щелкнуть слева направо;
В тексте вы получите события SPAN + INPUT,
между текстом и флажок, вы получите события LABEL + INPUT,
на флажок только для события INPUT,
затемдалее справа, только событие DIV.(поскольку DIV является блочным элементом и охватывает весь путь вправо)

Одним из решений будет прослушивание только <input> событий элемента, но тогда вы не будете захватывать <div> щелчков и вы также можетене помещайте <div> внутри <label>.

Самое простое, что нужно сделать, это игнорировать щелчки по <label> и всем активируемым элементам внутри <label>, за исключением <input>.В этом случае <span>.

const elWrapper = document.querySelector('.wrapper');

elWrapper.addEventListener('click', handleLabelClick);

function handleLabelClick(event) {
	console.log('Event received from tagName: '+event.target.tagName);
	if (event.target.tagName === 'LABEL' || event.target.tagName === 'SPAN') { return; }
	console.log('Performing some action only once. Triggered by click on: '+event.target.tagName);
}
<div class="wrapper">
  <label for="option1">
    <span>Select me</span>
    <input id="option1" type="checkbox">
  </label>
</div>

*) В моем примере я изменил имя класса parent на оболочку и имя переменной label который содержал элемент div для elWrapper , потому что parent и label являются ключевыми словами, и их использование для других средств может сбивать с толку.

Решение от @TJ приводит к тому, что события из <label> и все внутри него игнорируются в будущем.Чтобы вызвать событие, вам нужно будет нажать где-нибудь на <div>, но не прямо на текст или флажок.

Я добавил свой ответ, потому что я не думал, что это ОПнамерение.

Но даже для этого другого случая вы можете использовать подход, аналогичный предложенному выше.Проверка, является ли выбранный элемент именем DIV, затем разрешает дальнейшие действия.Я думаю, что это более просто, более локализовано (не влияет на распространение событий), более универсально (если вы выбираете захват: addEventListener (..., ..., true), это все равно будет работать)

0 голосов
/ 18 октября 2018

Насколько я понимаю, вы хотите, чтобы клики по элементам .parent запускали обработчик кликов, но вы не хотите, чтобы этот обработчик запускался для кликов, связанных с флажком или его меткой в ​​пределах .parent.

* 1004.* Два способа сделать это:
  1. Добавить обработчик для label, который вызывает stopPropagation или

  2. Проверка в событииобработчик того, прошло ли событие через label

Вот подход № 1:

const parent = document.querySelector('.parent');

parent.addEventListener('click', handleLabelClick);

// Stop clicks in the label or checkbox from propagating to parent
parent.querySelector("label").addEventListener("click", function(event) {
  event.stopPropagation();
});

function handleLabelClick(event) {
	console.log('Clicked');
}
.parent {
  border: 1px solid #ddd;
}
<div class="parent">
  <label for="option1">
    <span>Select me</span>
    <input id="option1" type="checkbox">
  </label>
</div>

Вот подход № 2:

const parent = document.querySelector('.parent');

parent.addEventListener('click', handleLabelClick);

function handleLabelClick(event) {
  const label = event.target.closest("label");
  if (label && this.contains(label)) {
    // Ignore this click
    return;
  }
	console.log('Clicked');
}
.parent {
  border: 1px solid #ddd;
}
<div class="parent">
  <label for="option1">
    <span>Select me</span>
    <input id="option1" type="checkbox">
  </label>
</div>
...