为什么jQuery选择事件监听器会触发多次?

21

请在 Google Chrome 浏览器中运行此示例

Stack Snippet

$(function() {
  $(":input").select(function() {
    $("div").text("Something was selected").show().fadeOut(1000);
    alert("Selected");
  });
  $("button").click(function() {
    $(":input").select();
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<button>Click To Select</button>
<input type="text" value="Some text">
<div></div>

为什么jQuery选择事件监听器会多次触发?有人知道背后的原因吗?在不使用超时的情况下,是否有任何解决方法来解决这个问题?


1
https://jsfiddle.net/arunpjohny/3qkvr5zq/2/ - 当它被 select() 调用内部触发后,在 click 处理程序退出之后,它会被调用两次,我认为这是由渲染操作完成的。 - Arun P Johny
3个回答

14
< p > $(":input")选择器也会选择按钮,因此会导致递归。请改用$("input")$(":input:not(button)")

我注意到当三个事件被触发时,第一个事件没有originalEvent属性,因此我们可以将其忽略,而后两个事件具有非常相似(但不完全相同)的时间戳。你可以将最后一个时间戳存储在某个变量中,并在事件侦听器中将其与事件的时间戳进行比较。如果这两个时间戳的舍入值相同,则可以忽略此事件。

$(function() {
  var lastTimeStamp;
  $("input").select(function(event) {
    if (!event.originalEvent ||
        lastTimeStamp === Math.round(event.timeStamp)) return;
    lastTimeStamp = Math.round(event.timeStamp);
    $("div").text("Something was selected").show().fadeOut(1000);
    alert("Selected");
  });
  $("button").click(function() {
    $("input").select();
  });
});

请查看更新后的JS Fiddle


10

问题似乎是以下两个因素的组合:

  • :input选择器会匹配到inputbutton,从而触发多个事件。
  • 即使只使用input作为选择器,也会在相关元素上触发一些奇怪的事件传播,导致select事件处理程序被多次调用。

为了避免以上两种情况,使用input作为选择器,并在事件处理程序中使用preventDefault()。根据HTML结构,可能还需要使用stopPropagation()

$(function() {
    $('input').select(function(e) {
        // e.stopPropagation(); // optional
        e.preventDefault();
        $('#message').text("Something was selected").show().fadeOut(1000);
        console.log('Selected');
    });

    $('button').click(function() {
        $('input').select();
    });
});

实际演示


错误的选择器确实是问题的根源,另一个问题只存在于Chrome中而不是FF和IE。 - A1rPun
3
e.preventDefault()会防止选择文本。 - John R

0

更新:我们都被愚弄了。select()函数需要一个prevent default。

Rory McCrossan找到了解决方法。干得好,伙计。

顺便说一句,我不确定select()的实际好处是什么!像focus()或on('focus',)这样的东西可能更有意义。不过我不确定上下文是什么。以下仍然适用:

为什么要浪费时间使用可能会改变的通用标签/类型选择器?使用ID,只选择你想要的那个。

如果你想检测多个,请使用class。如果你想使用多个,但想弄清楚你点击了哪一个,请使用class和ID。用class绑定,并使用$this.attr('id')进行标识。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<button>Click To Select</button>
<input type="text" value="Some text" id="pick-me">
<div></div>

$(function() {
  $("#pick-me").select(function(event) {
    event.preventDefault();
    $("div").text("Something was selected").show().fadeOut(1000);
    alert("Selected");
  });
  $("button").click(function() {
    $("#pick-me").select();
  });
});

1
最新版的Chrome浏览器上仍然会触发三次,参见JS Fiddle - Michał Perłakowski
@Gothdo 哈哈,你说得对。很奇怪。我现在要修复它! - Tim Ogilvy
是的,这是需要preventDefault事件。我甚至不确定我是否曾经在输入上使用过“select()”方法。我总是使用focus或类似的东西。 - Tim Ogilvy
1
@TimOgilvy 如果我们使用 event.preventDefault();,它将不允许通过按钮单击选择文本。 - John R
3
$e.focus() 将焦点放在 $e 元素上。 $e.select() 将焦点放在 $e 元素上并选择全部内容。 - Jose Rui Santos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接