为什么onclick元素会为label元素触发两次

IT技术 javascript html
2021-02-12 02:41:31

      window.onload = function(){
         var wow = document.getElementById("wow");
    	 wow.onclick = function(){
    	     alert("hi");
    	 }
      }
    <label id="wow"><input type="checkbox" name="checkbox" value="value">Text</label>

这是我的代码,当我单击“文本”时,它会提醒您两次,但是当我单击该框时,该onclick元素只会触发一次,为什么?

6个回答

当您单击标签时,它会触发单击处理程序,您会收到警报。

但是点击标签也会自动向关联的输入元素发送点击事件,因此这被视为点击复选框。然后事件冒泡会导致在包含元素(即标签)上触发 click 事件,因此您的处理程序将再次运行。

如果您将 HTML 更改为此,您将不会收到双重警报:

<input id="wowcb" type="checkbox" name="checkbox" value="value">
<label id="wow" for="wowcb">Text</label>

标签现在使用for属性与复选框相关联,而不是环绕它。

演示

@FelixKling 您无法将更改处理程序绑定到标签 AFAIK。
2021-03-17 02:41:31
我想我从来没有注意到这一点,因为我通常只会将“点击”处理程序放在 上<input>,而不是标签上。这是一种奇怪的行为,我不确定我能想出一个有用的理由。
2021-03-20 02:41:31
理想情况下,应该绑定一个更改事件处理程序。非常适合可访问性。
2021-03-29 02:41:31
@MMiller 是的,但是您必须为此在复选框上添加一个单击处理程序。
2021-03-31 02:41:31
您还可以停止事件传播。
2021-04-12 02:41:31

如果您只想响应标签上的点击而不是复选框上的点击,您可以查看event.target属性。它引用调用侦听器的元素,以便如果单击不在该元素上,则不要执行操作:

window.onload = function(){
  var el = document.getElementById('wow');
  el.addEventListener('click', function(event) {

    if (this === event.target) {
      /* click was on label */
      alert('click was on label');

    } else {
      /* click was on checkbox */
      event.stopPropagation();
      return false;
    }

  }, false);
}

另一方面,如果您只想响应点击复选框(点击标签也会产生点击复选框),然后执行相反的操作。对标签上的点击什么也不做,让复选框中的点击通过:

window.onload = function(){
  var el = document.getElementById('foolabel');
  el.addEventListener('click', function(event) {

    if (this === event.target) {
      /* click was on label */
      event.stopPropagation();
      return false;

    } else {

      /*
      ** click is from checkbox, initiated by click on label
      ** or checkbox                                  
      */

      alert('click from checkbox');
    }

  }, false);
}

这个版本似乎有最自然的行为。但是,更改标记以使标签不再包裹复选框将意味着不会调用侦听器。

事件气泡。

复选框是标签的子节点。您单击复选框。事件气泡到标签。然后警报弹出两次。

单击复选框时防止警报弹出两次。您可以将 onclick 功能更改为:

wow.onclick = function(e){
    alert('hi');
    stopBubble(e);
}


function stopBubble(e)  
{  
    if (e && e.stopPropagation)  
        e.stopPropagation()  
    else 
        window.event.cancelBubble=true 
}  

希望这对你有用。

弹出两次。一种来自点击标签。另一种是单击输入气泡到标签。我认为这可以解释这一点。:)
2021-03-15 02:41:31
我不认为这是正确的答案。包含<label>标记上的事件处理程序应该只接收一次冒泡事件。浏览器生成两个“点击”事件,一个以<input>元素为目标,一个以<label>为目标。
2021-03-16 02:41:31
不,冒泡只涉及传递给父元素上的事件处理程序的一个事件对象。在这种情况下,事件对象在两次调用处理程序时是不同的。
2021-03-17 02:41:31
它不起作用:jsfiddle.net/barmar/fMMAz/5从标签到复选框的传播没有冒泡,这是标签非常特定的东西。
2021-03-18 02:41:31
不,它没有。我点击了“文本”并在 Chrome 中收到了两个警报。
2021-03-20 02:41:31

Label 标签将与其中的输入标签相关联。所以当你点击标签时,它也会触发输入的点击事件,然后冒泡到标签本身。

看到这个:

document.getElementById("winput").addEventListener('click', function(event){
     alert('input click');

     //stop bubble
     event.stopPropagation();

 }, false); 

http://jsfiddle.net/96vPP/

再次一头扎进这个陷阱并决定证明它对自己做了什么,希望能帮助我记住。为了帮助未来的人,上面可能不够清楚,这里是我做的例子。

https://jsfiddle.net/ashes/0bcauenm/

$(".someBigContainer").on("click", "input[type=checkbox]", ()=> {
    $output.val("Clicked: checkbox\n" + $output.val());
}); 

编辑:添加了带有代码片段的链接。