为啥 jQuery 选择事件侦听器会触发多次?

Posted

技术标签:

【中文标题】为啥 jQuery 选择事件侦听器会触发多次?【英文标题】:Why is jQuery select event listener triggering multiple times?为什么 jQuery 选择事件侦听器会触发多次? 【发布时间】:2016-07-17 00:09:35 【问题描述】:

请在 Google Chrome 浏览器中运行this sample。

堆栈片段

$(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 选择事件监听器会触发多次?有谁知道这背后的原因?是否有任何不使用超时的解决方法?

【问题讨论】:

jsfiddle.net/arunpjohny/3qkvr5zq/2 - 一旦被select() 调用在内部触发,但在退出click 处理程序后,它会被调用两次,我认为这是由渲染操作完成的 【参考方案1】:

$(":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();
  );
);

见updated JS Fiddle。

【讨论】:

【参考方案2】:

问题似乎是以下几种情况的组合:

:input 选择器获取inputbutton,因此触发了多个事件。 即使仅使用 input 作为选择器,也会在相关元素上触发一些奇怪的事件传播,从而多次引发 select 事件处理程序。

为避免上述两种情况,请使用input 作为选择器,并在事件处理程序中使用preventDefault()stopPropagation() 也可能是必需的,具体取决于您的 html 结构。

$(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();
    );
);

Working example

【讨论】:

错误的选择器确实是问题的根源,另一个问题只存在于 Chrome 而不是 FF & IE。 @RoryMcCrossan e.preventDefault() 将阻止选择文本。【参考方案3】:

更新:我们都被愚弄了。 select() 函数需要一个阻止默认值。

Rory McCrossan 想通了。干得好伙计。

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

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

如果要检测多个,请使用一个类。如果您想使用多个,但要弄清楚您单击了哪个,请使用一个类和一个 ID。与类绑定,并使用$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();
  );
);

【讨论】:

在最新的 Chrome 上仍然会触发 3 次,请参阅 JS Fiddle。 @Gothdo 哈哈,你是对的。奇怪。现在要解决这个问题! 是的,这是需要 preventDefault 的事件。我什至不确定我是否曾经在输入上使用过'select()'方法。我总是使用焦点或类似的东西。 @TimOgilvy 如果我们使用event.preventDefault();,它将不允许通过按钮单击选择文本。 $e.focus() 将焦点放在 $e 元素上。 $e.select() 将焦点放在 $e 元素上并选择所有内容。

以上是关于为啥 jQuery 选择事件侦听器会触发多次?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 jQuery 单击事件侦听器未在 WooCommerce 按钮上注册?

使用名称中的点调度 CustomEvent 不会触发 jQuery.on() 事件侦听器

丢弃 child 会触发 parent 的“drop”事件侦听器。我想用特定于子的“drop”覆盖该事件侦听器

Jquery 事件侦听器不处理附加元素(他们应该,不是吗?)

为啥没有事件侦听器或引用的封闭 NetConnection 会留在内存中?

以编程方式在音频元素上设置当前 Date 属性会导致事件侦听器无限期触发