问题 为什么中间点击在几种情况下不会触发“点击”?


这是一个 的jsfiddle 我看到的行为,与中间点击和 click Chrome和FF中的活动。

'click'有点实际

方法1:绑一个 click 处理程序直接到一个 a 元素和中键单击将触发Chrome中的处理程序,但不会触发FF。

$('div a').on('click', function(ev) {
    // middle click triggers this handler
});

方法2:绑定委托 click 处理程序到 div 其中包含一个或多个 a。中间点击即可  在Chrome或FF中触发此处理程序。

$('div').on('click', 'a', function(ev) {
    // middle click doesn't trigger this handler
});

如果div开始为空,那么这种方法非常有价值 a 稍后通过AJAX调用填充元素,或者作为某些用户输入的结果填充。

'mouseup'有效

运用 mouseup 代替 click 导致方法1和2都在两个浏览器中都有效。

// Approach 1 w/ mouseup
$('div a').on('mouseup', function(ev) {
    // middle click **does** trigger this handler in Chrome and FF
});

// Approach 2 w/ mouseup
$('div').on('mouseup', 'a', function(ev) {
    // middle click **does** trigger this handler in Chrome and FF
});

这是 的jsfiddle 同 mouseup

这很有趣,在某些情况下可能有用,因为 mouseup 差不多 click。但 mouseup    click,我是在追求的行为 click。我不想创造一个hacky mousedown; setTimeout; mouseup 模拟 click

我很确定答案是“nope”,但是有一种跨浏览器的方式可以触发中键点击 click 处理程序?如果没有,原因是什么?


9687
2017-11-22 21:34


起源



答案:


通常会针对鼠标左键触发click事件,但是,根据浏览器,右侧和/或中间按钮可能会也可能不会发生单击事件。

在Internet Explorer和Firefox中,不会为右侧或中间按钮触发click事件。

因此,我们无法在中间或右侧按钮上可靠地使用事件处理程序的click事件。

相反,要区分鼠标按钮,我们必须使用mousedown和mouseup事件,因为大多数浏览器会为任何鼠标按钮触发mousedown和mouseup事件。

在Firefox和Chrome中 event.which 应该包含一个数字,表示按下了什么鼠标按钮(1为左,2为中,3为右)。

另一方面,在Internet Explorer中, event.button 指示单击了什么鼠标按钮(1为左,4为中,2为右);

event.button 也应该在Firefox和其他浏览器中工作,但数字可能略有不同(0为左,1为中,2为右)。

所以把它放在一起我们通常做这样的事情:

document.onmousedown = function(e) {
    var evt = e==null ? event : e;

    if (evt.which) { // if e.which, use 2 for middle button
        if (evt.which === 2) {
            // middle button clicked
        }
    } else if (evt.button) { // and if e.button, use 4
        if (evt.button === 4) {
            // middle button clicked
        }
    }
}

随着jQuery规范化 event.which,你应该只需要在jQuery事件处理程序中使用它,因此这样做:

$('div a').on('mousedown', function(e) {
    if (e.which === 2) {
        // middle button clicked           
    }
});

换句话说,你不能使用onclick事件,所以要模拟它,你可以使用mousedown和mouseup。

您可以添加一个计时器来限制mousedown和mouseup事件之间允许的时间,甚至可以使用mousemove处理程序来限制mousedown和mouseup事件之间的移动,并且如果鼠标指针移动了十次以上,则不会触发事件处理程序像素等可能性几乎是无穷无尽的,所以这不应该是一个问题。

$('#test').on({
    mousedown: function(e) {
        if (e.which === 2) {
            $(this).data('down', true);
        }
    },
    mouseup: function(e) {
        if (e.which === 2 && $(this).data('down')) {
            alert('middle button clicked');
            $(this).data('down', false);
        }
    }
});

7
2017-11-22 22:14



我很感激你的答案,但我在“我不想创造一个hacky”的问题中说明了这一点 mousedown; setTimeout; mouseup 模拟 click“重新发现点击似乎是一个坏主意,但我会再次权衡利弊。 - mwcz
我在答案中说,这是捕获中间按钮点击的唯一可靠方法,没有其他选择。 - adeneo
是否有必要使用===比较而不是==?为什么?谢谢。 - ElTête
@ElTête - 不是 neccessary但是作为 e.which 总是返回一个数字,为​​什么不直接比较一个数字。 - adeneo


答案:


通常会针对鼠标左键触发click事件,但是,根据浏览器,右侧和/或中间按钮可能会也可能不会发生单击事件。

在Internet Explorer和Firefox中,不会为右侧或中间按钮触发click事件。

因此,我们无法在中间或右侧按钮上可靠地使用事件处理程序的click事件。

相反,要区分鼠标按钮,我们必须使用mousedown和mouseup事件,因为大多数浏览器会为任何鼠标按钮触发mousedown和mouseup事件。

在Firefox和Chrome中 event.which 应该包含一个数字,表示按下了什么鼠标按钮(1为左,2为中,3为右)。

另一方面,在Internet Explorer中, event.button 指示单击了什么鼠标按钮(1为左,4为中,2为右);

event.button 也应该在Firefox和其他浏览器中工作,但数字可能略有不同(0为左,1为中,2为右)。

所以把它放在一起我们通常做这样的事情:

document.onmousedown = function(e) {
    var evt = e==null ? event : e;

    if (evt.which) { // if e.which, use 2 for middle button
        if (evt.which === 2) {
            // middle button clicked
        }
    } else if (evt.button) { // and if e.button, use 4
        if (evt.button === 4) {
            // middle button clicked
        }
    }
}

随着jQuery规范化 event.which,你应该只需要在jQuery事件处理程序中使用它,因此这样做:

$('div a').on('mousedown', function(e) {
    if (e.which === 2) {
        // middle button clicked           
    }
});

换句话说,你不能使用onclick事件,所以要模拟它,你可以使用mousedown和mouseup。

您可以添加一个计时器来限制mousedown和mouseup事件之间允许的时间,甚至可以使用mousemove处理程序来限制mousedown和mouseup事件之间的移动,并且如果鼠标指针移动了十次以上,则不会触发事件处理程序像素等可能性几乎是无穷无尽的,所以这不应该是一个问题。

$('#test').on({
    mousedown: function(e) {
        if (e.which === 2) {
            $(this).data('down', true);
        }
    },
    mouseup: function(e) {
        if (e.which === 2 && $(this).data('down')) {
            alert('middle button clicked');
            $(this).data('down', false);
        }
    }
});

7
2017-11-22 22:14



我很感激你的答案,但我在“我不想创造一个hacky”的问题中说明了这一点 mousedown; setTimeout; mouseup 模拟 click“重新发现点击似乎是一个坏主意,但我会再次权衡利弊。 - mwcz
我在答案中说,这是捕获中间按钮点击的唯一可靠方法,没有其他选择。 - adeneo
是否有必要使用===比较而不是==?为什么?谢谢。 - ElTête
@ElTête - 不是 neccessary但是作为 e.which 总是返回一个数字,为​​什么不直接比较一个数字。 - adeneo


简短回答:不。

问题是,您希望捕获中间点击的内容是什么?中间点击不是要与当前页面交互,而是在新选项卡中打开链接。

Chrome目前正在努力放弃此行为: https://code.google.com/p/chromium/issues/detail?id=255

目前有关于此主题的w3c邮件列表的一般性讨论: http://lists.w3.org/Archives/Public/www-dom/2013JulSep/0203.html


但就目前而言,您可以在文档级别上捕获Firefox中的middleclicks:

$(document).on('click', function(e){
    console.log(e);
});

4
2017-11-22 22:03



非常好,感谢您的背景信息。用例是捕获中间点击的分析数据(就像ctrl +点击一样)。按住Ctrl +单击可触发onclick处理程序。 - mwcz
Chromium问题255, 评论160 谈论一个新的事件 auxclick,和 这个请求在caniuse网站上 包括更有用的链接。 - Denilson Sá Maia


我已经建立了一个 工厂用于使用vanilla JS创建鼠标中键处理程序,并使用最新的Firefox和Chrome

const MiddleClickHandlerFactory = (node, handlerFn) => {
  node.addEventListener('mousedown', e => {
    if (e.button !== 1) return;
    e.preventDefault();   // stop default scrolling crap! Instead install ScrollAnywhere!
    const originalTarget = e.target;
    document.addEventListener('mouseup', e => {   // register on DOCUMENT to be sure it will fire even if we release it somewhere else
      if (e.target.isSameNode(originalTarget)) handlerFn(e);
    }, {capture: true, once: true, passive: true});
  }, true)
};

0
2018-04-19 07:47