【问题标题】:Jquery click event triggers twice when clicked on html label点击html标签时Jquery点击事件触发两次
【发布时间】:2013-06-15 15:14:19
【问题描述】:

我有一个 div 容器,其中包含 html 复选框及其标签。 使用 jquery 我想在有人单击此容器中的标签时触发单击事件。

当我点击标签时,我看到 jquery 点击事件触发了两次!

出于测试目的,我在复选框而不是标签上触发了点击事件 并且这里的复选框点击事件只触发一次。

这是小提琴。 http://jsfiddle.net/AnNvJ/2/

<tr>
    <td>
        <div id="test">
            <label>
                <input type="checkbox" id="1" />demo1</label>
            <label>
                <input type="checkbox" id="2" />demo2</label>
            <label>
                <input type="checkbox" id="3" />demo3</label>
        </div>
    </td>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
</tr>

JQUERY

$(document).ready(function () {

    $('#test label').live('click', function (event) {
        alert('clicked');
    });


    $('#test checkbox').live('click', function (event) {
        alert('clicked');
    });

});

【问题讨论】:

  • 这是因为复选框是附加事件的正确元素,而不是标签,change 事件通常是正确使用的事件,而不是 click
  • 因为您的输入复选框在标签内。
  • 谢谢大家的回复,我从标签内部删除了输入,它解决了这个问题,但是当我点击标签时它不会切换检查。我发现通过在标签中使用 for 属性可以切换检查,例如

标签: jquery events click


【解决方案1】:

永远不会调用$('#test checkbox') 之一,因为您没有名称为checkbox 的标签。

根据您是点击复选框还是标签,$('#test label') 的回调会被调用一次或两次因为冒泡,因为输入元素是标签的一部分并且是一个>union,因此如果标签被点击,也会收到事件(它没有冒泡,你不能这样做event.stopPropagation())。

如果您以这种方式更改代码,您可以检查一下:

 $('#test label').live('click', function (event) {
        event.stopPropagation(); //<-- has no effect to the described behavior
        console.log(event.target.tagName);
 });

点击标签:

标签
输入

点击输入:

输入

编辑 如果您只想处理对 label 的点击而不是复选框 - 而且您无法更改 HTML 结构 - 您可以忽略 input 元素的事件。

无论哪种方式:

$('#test label').live('click', function (event) {
    if( event.target.tagName === "LABEL" ) {
         alert('clicked');
    }
});

或者使用jQuery来测试:

$('#test label').live('click', function (event) {
    if( $(event.target).is("label") ) {
         alert('clicked');
    }
});

【讨论】:

  • +1 表示这里没有冒泡,但这很棘手,因为联合,他肯定需要拆分这些
  • @sravis 添加了一个简短的示例,您可以如何在不更改 html 代码的情况下进行过滤。
  • @t.niese,检查下面我的答案中的代码,它避免了双重触发并且复选框保持其行为。
  • 冒泡导致标签上的点击处理程序在点击标签时被调用两次。在这种情况下,会为标签触发点击事件。由于标签与输入相关联,因此单击标签的默认处理会为输入生成另一个单击事件,如果输入是标签的后代,则第二次单击会冒泡到标签。所以当然添加event.stopPropagation() 没有效果——事件已经从输入冒泡到标签。
【解决方案2】:

这是因为您将输入放在标签内,所以当您单击复选框时,您还会单击每个父母(这称为冒泡) 编辑,感谢@t.niese:实际上没有冒泡在这里,因为问题是当您单击标签时,它只会冒泡。

要防止双击事件,还要选中您可以使用的复选框:

$(document).ready(function () {

    $('#test label').on('click', function (event) {
        event.preventDefault();
        var $check = $(':checkbox', this);
        $check.prop('checked', !$check.prop('checked'));
        alert('clicked label');
    });


    $('#test :checkbox').on('click', function (event) {
        event.stopPropagation();
        alert('clicked checkbox');
    });
});

FIDDLE

也更喜欢$.on 的用法,并注意:checkbox selector[type="checkbox"] 的用法,根据JQuery API 更快,更兼容(attribute selectors

event.preventDefault() 将停止浏览器为原生标记处理的所有操作 event.stopPropagation() 防止冒泡和任何父处理程序收到事件通知

【讨论】:

  • 如果建议您使用 .on 而不是 .live 您必须提供等效代码,例如 $('#test').on('click', 'label', function (event) {
  • 嗨,为了我自己的利益,我查看了您发布的内容,jsfiddle.net/uUZyn。仅单击 ckecbox 就像一个魅力,但是当单击标签时也会触发复选框警报,这是有意的吗?
  • @Chococroc 是的,结帐 t.niese 在那里发帖,他解释了原因。
  • @TecHunter 是的,就像 t.niese 说的,event.stopPropagation();不管用。但正如你所说,我从标签中删除了输入字段,现在它工作正常。但是在这里,如果用户单击标签,它将不会选中该复选框。有什么解决办法?
  • @sravis 你去吧,你可以将输入保存在标签内,从而保持一些逻辑层次结构
【解决方案3】:

OP 找到了一种解决方案来防止标签的单击事件处理程序在一次单击时执行两次。选择的答案也有很好的解决方案,但它错误地指出这与事件冒泡无关。

有两次点击的原因确实与事件冒泡有关。

单击与输入关联的标签会触发两个单击事件。为标签触发第一次点击事件。该单击事件的默认处理会导致为关联的输入触发第二个单击事件。如果输入是标签的后代,则第二个点击事件会冒泡到标签。这就是标签的 click 事件处理程序被调用两次的原因。


因此,解决此问题的一种方法(正如 OP 发现的那样)是不要将输入放在标签元素内。仍然会有两个点击事件,但是第二个点击事件不会从输入冒泡到标签。

当输入不在标签内时,可以使用标签的“for”属性将标签与输入相关联。 “for”属性必须设置为输入的“id”值。

<input type="checkbox" id="1" />
<label for="1">demo1</label>

另一个解决方案是阻止事件从输入冒泡:

$('#test :checkbox').on('click', function(event) {
    event.stopPropagation();
});

$('#test label').on('click', function() {
    alert('clicked');
});

然后在所选答案中提出了解决方案,即当标签从输入中冒出时,让标签的点击处理程序忽略该事件。不过我更喜欢这样写:

$('#test label').on('click', function(event) {
    if (event.target == this) {
        alert('clicked');
    }
});

【讨论】:

    【解决方案4】:

    您必须在标签中使用 preventDefault 并在复选框中使用 stopPropagation:

    http://jsfiddle.net/uUZyn/5/

    代码:

    $(document).ready(function () {
    
        $('#test label').on('click', function (event) {
          event.preventDefault();        
          alert('clicked label');
          var ele = $(this).find('input');
        if(ele.is(':checked')){
          ele.prop('checked', false);        
        }else{
          ele.prop('checked', true);
        }
      });
    
      $('#test :checkbox').on('click', function (event) {
        event.stopPropagation();
        alert('clicked checkbox');
      });
    });
    

    这样可以避免@t.niese 解释的行为

    感谢您之前的回答,如果没有它们,我将无法弄清楚,:D。

    更新

    正如 t.niese 指出的那样,这是我用行为更新的答案:

    http://jsfiddle.net/uUZyn/6/

    刚刚添加了使用 preventDefault 后的检查行为 呵呵

    【讨论】:

    • 抱歉删除了评论而不是编辑 :D 如果你这样做,当你点击标签时复选框不会切换,但这是建议的行为。
    • 我发现通过在标签中使用 for 属性可以切换检查,例如
    • @Chococroc 工作......但有点复杂;)如果简短检查目标是否为label 就足够了:D
    • 哦对了,当你有锤子的时候,一切看起来都像钉子,哈哈哈,:D
    • @t.niese,好点子,但是在检查之后,(jsfiddle.net/uUZyn/8)如果你不做所有的解决方法它不起作用,你可以检查一下吗?也许我对添加标签的方式有误但不确定......
    【解决方案5】:

    我刚刚遇到了同样的问题,点击标签会触发两个单独的点击事件。通过将name 属性添加到输入并将for 属性添加到标签,我能够为自己解决问题。

    例如:

    <div id="test">
        <label for="checkbox1">
            <input type="checkbox" name="checkbox1" id="1" />demo1</label>
        <label for="checkbox2">
            <input type="checkbox" name="checkbox2" id="2" />demo2</label>
        <label for="checkbox3">
            <input type="checkbox" name="checkbox3" id="3" />demo3</label>
     </div>
    

    无需 JavaScript。

    【讨论】:

    • for 属性的值应该是id 值,而不是name 值。您所做的是您已将 &lt;label&gt; 元素与包含的 &lt;input&gt; 元素解除关联。这就是为什么只触发一次点击事件的原因,但这也意味着点击标签不会导致复选框发生变化。 jsfiddle
    【解决方案6】:

    这主要发生在您为具有子元素 Y 的 X 元素触发“点击”以及用户点击 Y 时。

    从实际点击的元素冒出的事件是您不想要的不想要的事件。

    您有两种选择来避免这种双重触发...

    1) 使用 $('selector').triggerHandler('any_standard_event_name') - 这里的标准事件是指“点击”、“更改”、“悬停”等(我从未尝试过此选项)

    2) 使用自定义事件名称。例如。 $('selector').trigger('my.click');

    阅读更多http://api.jquery.com/trigger/

    【讨论】:

      【解决方案7】:

      关于我的情况以及我使用 Material Design 的 md 按钮这一事实,上面的建议效果不佳,因为它大大减少了我的 android 上接收手指按压的区域的占地面积。更不用说我有时会得到超过 3 个标记名,而不是 2 个标记名(按钮、跨度或 MD-ICON)。此外,网络上其他地方更改我的 Aria 版本的建议也无济于事。我最终发现问题是我在 angular.js 之前加载了 jquery,即使 ng-infinate-scroll 的帮助文档说要先加载 jquery。无论如何,如果我在 angular ng-infinate-scroll 之后尝试加载 jquery 将停止工作。所以我下载了最新版本的 ng-infinate-scroll(非稳定)版本 1.2.0,现在我可以在 angular.js 之后加载我的 jquery,我的无限滚动可以解决我在多次触发 ng-click 时遇到的所有问题走了。

      【讨论】:

        【解决方案8】:

        我的元素也相互包裹。

        所以我在这个解决方案中使用了一个简单的 CSS 技巧:

        .off{
           pointer-events:none;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-08-20
          相关资源
          最近更新 更多