为什么在某些情况下中键单击不能触发“点击”?

14
这里有一个与Chrome和FF中的中键单击和“click”事件相关的行为的JSFiddle示例。

'click'有点儿奇怪

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

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

方法二:将委托的click处理程序绑定到包含一个或多个adiv上。在Chrome或FF中,中键单击不会触发此处理程序。

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

如果

最初为空,a元素后来通过AJAX调用或某些用户输入填充,则此方法非常有价值。

使用'mouseup'

使用mouseup而不是click会使得两种方法在两种浏览器中都起作用。

// 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
});

这里是带有 mouseupJSFiddle

这很有趣,某些情况下可能会有用,因为mouseup几乎等同于click。 但是mouseup并不等同于click,我想要的是click的行为。 我不想创建一个hacky的mousedown; setTimeout; mouseup来模拟click

我相当确定答案是否定的,但是否有一种跨浏览器的方法可以让鼠标中键触发click处理程序? 如果没有,原因是什么?

3个回答

11

点击事件通常由左鼠标按钮触发,但是根据不同的浏览器,右键和/或中键可能会触发或不触发点击事件。

在Internet Explorer和Firefox中,对于右键或中键,不会触发点击事件。

因此,在中键或右键上使用事件处理程序时,我们不能可靠地使用点击事件。

相反,为了区分鼠标按钮,我们必须使用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);
        }
    }
});

2
我很感谢你的回答,但是我在问题中已经明确表示“我不想创建一个模拟click的笨拙的mousedown; setTimeout; mouseup方法。”重新发明点击似乎不是一个好主意,但我会再次权衡利弊。 - mwcz
1
我在我的回答中指出,这是捕获中间按钮点击的唯一可靠方法,没有其他替代方案。 - adeneo
需要使用 === 比较符号而不是 == 吗?为什么?谢谢。 - ElTête
1
@ElTête - 这不是必要的,但由于e.which始终返回一个数字,为什么不直接与一个数字进行严格比较呢? - adeneo

3

简短回答:不行。

问题是,你想要捕获中间点击做什么?中间点击并不是用来与当前页面交互,而是打开一个新标签页的链接。

Chrome目前也在放弃这种行为:https://code.google.com/p/chromium/issues/detail?id=255

同时,w3c邮件列表上也有关于这个话题的讨论:http://lists.w3.org/Archives/Public/www-dom/2013JulSep/0203.html


然而,现在你可以在Firefox上文档级别捕获中间点击:

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

非常好,感谢提供的背景信息。用例是捕获中键点击的分析数据(就像ctrl +点击)。Ctrl + 点击确实会触发onclick处理程序。 - mwcz
Chromium问题255,评论160谈论了一个新的事件auxclick,而caniuse网站中的请求则包含了更多有用的链接。 - Denilson Sá Maia
嗨@BojidarStanchev,我的意图并不是以任何方式居高临下或傲慢自大 - 我肯定不知道每个用例。我应该将这个问题作为对原始问题的评论而不是回答的一部分来编写。有道理,感谢反馈。同时,请注意,问题和答案已经有近七年的历史了 - 因此我不会更新它。 - Max

0
我已经使用原生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)
};

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