8

我试图让一个事件同时在click和上触发focus,但是我只希望它触发一次。当我在输入内部单击时,它会触发两次(单击和聚焦)。我怎样才能防止这种情况?

$('input').on('focus click', function(){
    console.log('fired');
});
4

3 回答 3

10

你可以.one改用。这将只允许事件触发一次,但也将在触发后删除绑定:

$('input').one('focus click', function(){
    console.log('fired');
});

如果您需要保持绑定,则必须跟踪鼠标按钮的状态和触发的当前目标mousedown

var mouseDown, currentTarget;
$('input').on({
    "mousedown mouseup": function (e) {
        mouseDown = e.type === "mousedown";
        currentTarget = e.target;
    },
    "focus click": function (e) {
        if (mouseDown && currentTarget === e.target) return;
        console.log('fired');
    }
});

请参阅jsFiddle 上的测试用例

于 2013-07-08T16:41:15.407 回答
8

一点滞后可能是一种选择。基本上记录您最后一次响应任一事件的时间,并在警戒时间内忽略后续事件。

可以为此使用 jQuery data(答案末尾的示例),但我更喜欢这个:A general-purpose debouncer:

click使用andfocus |的实时示例 直播源

$("#field").on("click focus", debounce(100, function(e) {
  // Event occurred, but not with 100ms of the previous one
}));

debouncer功能:

// debounce - debounces a function call
//
// Usage: var f = debounce([guardTime, ] func);
//
// Where `guardTime` is the interval during which to suppress
// repeated calls, and `func` in the function to call.
// You use the returned function instead of `func` to get
// debouncing;
//
// Example: Debouncing a jQuery `click` event so if it happens
// more than once within a second (1,000ms), subsequent ones
// are ignored:
//
//    $("selector").on("click", debounce(1000, function(e) {
//      // Click occurred, but not within 1000ms of previous
//    });
//
// Both `this` and arguments are passed through.
function debounce(guardTime, func) {
  var last = 0;

  if (typeof guardTime === "function") {
    func = guardTime;
    guardTime = 100;
  }
  if (!guardTime) {
    throw "No function given to debounce";
  }
  if (!func) {
    throw "No func given to debounce";
  }

  return function() {
    var now = +new Date();
    if (!last || (now - last) > guardTime) {
      last = now;
      return func.apply(this, arguments);
    }
  };
}

(“去抖动器”这个名称是使用滞后来限制输入的常用术语。IIRC,它来自“开关去抖动器”,这是一个(非常)简单的电路,用于避免像机械一样触发动作数百次-throw 电气开关从打开转换为关闭,反之亦然,因为当触点靠近时,在开关达到稳态之前可能会有很多关闭/打开/关闭/打开/关闭/打开的颤动。这种喋喋不休被称为“弹跳”,因此称为“去抖动”。)


仅使用 jQuery 的方法data

$('input').on('focus click', function(){
    var $this = $(this);
    var now = +new Date();
    var lastClicked = $this.data("lastClicked");
    if (lastClicked && (now - lastClicked) < 100) {
        // Don't do anything
        return;
    }
    $this.data("lastClicked", now);

    // Do the work
});
于 2013-07-08T16:41:30.823 回答
1

这是一个老问题,但我找不到任何其他答案,比如解决我的问题。因此,我将其发布在此处,以供 2015 年碰巧遇到此问题的任何人使用。

$('#menu-button').on('click focus', function() {
  if(!$(this).is(':focus')) { // 1
    // Do stuff once
  }
  else {
    $this.blur(); // 2
  }
});
  1. 这仅在单击时触发事件。我不确定幕后发生了什么,所以也许有人可以向我解释这一点,但是标签和焦点似乎没有受到影响并且 100% 正常工作。

  2. 这会使所选对象失去焦点,但会将焦点路径设置回文档的顶部。我把它留在这里,这样我就可以再次单击选定的元素来禁用菜单。我仍在寻找解决方法以保持焦点路径。

编辑:更好的方法:

$('#menu-button').on('click focus', function(event) {
  if(event.type === 'focus') { // 1
    // Do stuff once
  }
});
  1. 点击会触发焦点,但焦点永远不会触发点击。所以只需在焦点事件上运行代码。
于 2015-03-04T06:48:05.060 回答