单击label元素内的文本时,Javascript事件侦听器将触发两次

问题描述 投票:0回答:2

单击标签内的文本时,如何使函数不会触发两次。

如果我使用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>
javascript label addeventlistener
2个回答
2
投票

据我了解,您希望点击.parent元素来触发点击处理程序,但您不希望该处理程序针对与.parent中的复选框或其标签相关的点击而触发。

有两种方法可以做到:

  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>

0
投票

这是标准(不幸)浏览器行为。 属性for<label>分配给<input>,因此当单击<label>元素时,浏览器将在您实际点击后立即模拟<input>元素上的单击。 这允许文本<input>的焦点,无线电<input>的切换,或复选框<input>的切换。 但是对于不幸的部分,这也导致两个元素都发送click事件。如果您正在收听父元素的点击,这意味着您将收到两次“人工互动”。一次来自<input>,一次来自“点击元素”。

对于那些想知道<input> could be inside <label>元素的人来说,你可以获得更少的造型可能性,但是你可以在复选框和文本之间点击。

你在<span>里面放置<input><label>实际上创造了一个不错的测试用例。 尝试从左到右点击; 在文本中,您将收到SPAN + INPUT事件, 在文本和复选框之间,您将获得LABEL + INPUT事件, 在复选框上直接只有INPUT事件, 然后更右,只有DIV事件。 (因为DIV是一个块元素并且一直向右移动)

一个解决方案是只听<input>元素事件,但是你不会捕获<div>点击,你也不能把<div>放在<label>

最简单的方法是忽略qazxsw poi上的点击以及qazxsw poi中除qazxsw poi之外的所有可点击元素。在这种情况下<label>

<label>
<input>

*)在我的例子中,我将类名parent更改为包含div元素和包含div元素的变量名标签到elWrapper,因为parent和label是关键字,并且它们用于其他方法可能会令人困惑。

来自@ T.J的解决方案。导致来自<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); }上的某个位置,但不要直接单击文本或复选框。

我添加了我的答案,因为我不认为这是OP的意图。

但即使对于其他情况,您也可以使用我上面提到的类似方法。检查点击的元素名称是否为DIV,然后允许进一步的操作。我认为它更直接,更局部化(不影响事件传播),更通用(如果你选择捕获:addEventListener(...,...,true)这仍然有用)

© www.soinside.com 2019 - 2024. All rights reserved.