使用jQuery检测中间按钮点击(滚动按钮)

22

我有一个包含a标签的列表,点击这些标签可以播放一些mp3文件。当使用jQuery绑定'click'事件时,它可以正常工作:

$oo.data({'__mp3play':true,'wapiHandle':h+0,'wapiIndex':o.ajaxPlayList[h].length})
           .bind( 'click', function()
            { var wh = $j(this).data('wapiHandle');
              if( typeof o.regObjects[wh] == 'object' && o.regObjects[wh].play(this.href))
               { return false; }
            });

点击鼠标左键时: 如果我的Flash插件已加载,则它会禁用默认处理;否则,将按正常方式打开。

但是: 当我使用鼠标的滚轮按钮并单击它时,点击事件不会触发,链接会正常打开。

我尝试过使用mousedown或mouseup事件,但没有帮助,该链接总是以正常方式打开,副作用是音乐也开始播放了。

preventDefault()完全不起作用。

有人能告诉我如何检测中间鼠标按钮的点击(滚动按钮的点击)吗?

感谢您的评论。

PS:我已经尝试过此站点上提供的其他关于“中间按钮”的解决方案。

在各种浏览器中测试,结果相同。

编辑: 这也不起作用,使用中间鼠标按钮将打开链接。使用左键时,什么也不会发生。

$oo.bind( 'mousedown click mouseup', function(e)
{ e.preventDefault(); e.stopPropagation(); return false; });

3
你为什么期望中间点击会触发“点击”事件呢?它们不同 - 你也不会期待右键点击会触发“点击”事件吧?你尝试使用“mousedown”方法做了什么?你有检查过“event.which”属性来确定是哪个按钮被按下了吗? - no.good.at.coding
中间点击将被处理为左键单击,A标签将被打开,因此您可以假设左键单击与中键单击相同。如果不是这样的话,就像你刚才说的那样,中键单击事件的名称是什么?Event.which没有任何区别,您可以测试中间按钮是否被按下,但您无法抑制中间按钮的默认行为,因为还涉及另一个事件?链接仍然会正常打开。 - Codebeat
您的假设是错误的-仅仅因为左键单击表现出类似于中键单击的行为并不意味着它们在内部是相同的。实际上,它们并不相同 - 在单击中间鼠标按钮时不会触发“click”事件。然而,你说的 preventDefault()stopPropagation() 没有任何效果是正确的,我暂时想不到任何解决方法。这里是一个fiddle,展示了我使用 mousedownmouseup 的进展情况。 - no.good.at.coding
请查看我的答案和下面的代码示例,我找到了一个论坛帖子,提供了一种使其正常工作的方法。 - no.good.at.coding
1
@AdvaitJunnarkar 不,因为已接受的答案提供了相同的解决方案(检测)并且还抑制了仅限于 IE 的行为(仅检测是不够的)。这是一个相当古老的问题,已经有8年历史并部分与IE相关。IE已经死了,成为历史,那么为什么要费心去找“新”的解决方案呢?已经有更好、更先进的解决方案了。 - Codebeat
显示剩余3条评论
7个回答

56

经过快速测试之后,它们的顺序如下:

  • 左 - 1
  • 中 - 2
  • 右 - 3

所以如果你有:

$(document).mousedown(function(e){
    switch(e.which)
    {
        case 1:
            //left Click
        break;
        case 2:
            //middle Click
        break;
        case 3:
            //right Click
        break;
    }
    return true;// to allow the browser to know that we handled it.
});

4
我知道,我知道,但这并不是问题的情况。 - Codebeat

17
请不要在mousedown事件期间触发点击操作。这会忽略其余的事件流,并违反当前用户期望和设计实践。
问题
以下是每种点击类型触发的事件正常顺序:
$(document).on("mousedown mouseup click focus blur",function(e) {
  console.log("{" + e.which + ":" + e.type + "}"); 
});    

Click Events

通常,我们处理最后一个click事件,因为它表示用户最终意图执行当前操作。

click事件在指针设备按钮(通常是鼠标的主按钮)在单个元素上按下并释放时触发。

不幸的是,中间鼠标按键不会触发这样的事件(可能是因为这样做会强制开发人员监听点击事件以区分多个可能的调用)。但是,如果我们想要连接到中间点击的操作,我们应该遵循相同的UX期望。
解决方案
我们将侦听mousedown事件,并立即附加一次性使用处理程序以处理mouseup事件。如果按下了中间键并且元素匹配,我们将trigger自己的custom event类型为middleclick的事件,我们将seed from the original event
$(document).on("mousedown", function (e1) {
  $(document).one("mouseup", function (e2) {
    if (e1.which == 2 && e1.target == e2.target) {
      var e3 = $.event.fix(e2);
      e3.type = "middleclick";
      $(e2.target).trigger(e3)
    }
  });
});

这将有助于区分确定中间按钮是否被点击以及我们希望在这种情况下如何处理它,因此我们可以像这样设置自定义事件类型的监听器:

$(document).on("middleclick", function (e) {
    console.log("{" + e.target.nodeName.toLowerCase() + ":" + e.type + "}"); 
});

jsFiddle和Stack Snippets中的演示:

$(document).on("mousedown", function (e1) {
  $(document).one("mouseup", function (e2) {
    if (e1.which == 2 && e1.target == e2.target) {
     var e3 = $.event.fix(e2);
      e3.type = "middleclick";
      $(e2.target).trigger(e3)
    }
  });
});

$(document).on("middleclick", function (e) {
  console.log("{" + e.target.nodeName.toLowerCase() + ":" + e.type + "}"); 
});

$(document).on("mousedown mouseup click focus blur",function(e) {
  console.log("{" + e.which + ":" + e.type + "}"); 
}); 
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.js"></script>

<div  style="background:#31965a;color:white;width:100px;height:100px;line-height:100px;text-align:center;">
  Click Me!
</div>

* 在Chrome、FF、Edge和IE11中测试通过


感谢您的回答,但是还有一些问题。您写道:“请不要在mousedown事件期间触发click操作。这会忽略其余的事件管道,并违反当前用户期望和设计实践。”嗯,首先,它不会触发任何东西,它本身不会引起硬件事件,也不会触发click事件,但它处理一个事件并可以取消一个事件。因为您没有取消到click的路线上的任何内容,所以当有其他附加的事件处理程序时,可能会导致不可预测的结果。我已经发布了一个解决方案/解决方法2。 - Codebeat
@Codebeat,感谢您的评论(但不确定为什么要点踩)。我不确定我们的解决方案是否旨在解决相同的问题。我的观点是,这个答案实际上处理了一个中间的鼠标点击事件,而不仅仅是一个中间的鼠标按下事件。当用户在同一元素上单击并释放时,会发生单击事件。当您在链接上进行中间鼠标单击时,在所有浏览器中,它将在您松开鼠标时打开该链接,而不是在您按下按钮时打开。这给了您取消的时间。如果您想要复制此功能,则需要此asr。 - KyleMit
这个问题已经有7年了,回答也是。我相信我之所以使用'$.browser.msie?"mousedown":"click"'是有原因的。同时像你一样,我也使用文档元素预览动作,所以在事件到达焦点元素之前就会取消该事件。这就是我取消事件的原因。如果你取消了一个事件,所有其他相关事件都将被忽略,因此没有单击事件,因为动作还没有完成。在你的示例中,你什么也没有取消,这就是为什么你仍然会在文档和焦点元素上收到所有事件的原因。目标是在操作被处理之前抑制行为。 - Codebeat
我看到我的问题和昨天的回答被踩了,希望你不是那个这么生气和沮丧的人。我不知道你的问题到底是什么,为什么要这样做,还有额外的处理程序等等...你的回答必须是一个评论。7年前mousedown(IE)和click(其他浏览器)之间存在差异是有原因的。再次强调,目标是在任何链接上执行操作之前抑制硬件行为(IE在7年前需要),并避免在每个元素上绑定额外的事件处理程序。... - Codebeat
如果您不取消事件,它就根本不会起作用。我也不触发点击事件等。实际上,我并不理解您的观点是什么,也许您不理解事件的工作原理。而且,这也不是问题的答案。您想讨论答案而不是问题,这可以作为一条评论。 - Codebeat
啊哈(嘭),我觉得你不理解这一行代码:'$ .browser.msie?"mousedown":"click"',它实际上是一个if-then-else语句。我不会同时绑定mousedown和click事件。对于IE,我在“mousedown”上处理它,在其他浏览器上则在“click”上处理。这就是你的观点所在,你认为我两个都处理了,但事实并非如此。 - Codebeat

8

好的,我想我明白了。这是一个看起来可行的fiddle。诀窍(至少在FF4中)似乎是将单击处理程序绑定到document并使其停止传播。

$(document).click(function(e){
        //not checking for the right click will prevent the context-menu from showing, you may or may not want to do that
        if (e.which != 3) { 
            e.preventDefault();
            return false;
        }
    });

这个解决方案是在这个论坛页面上找到的


3

好的,大家好:

感谢您的建议。'@no.good.at.coding' 提供了一个不错的解决方案,但在IE浏览器上无法运行(请参见本文后面的内容),但这是一个很好的起点。我将他的代码更改为以下内容:

// Avoid relations will be opened in extra tab when clicking middle-scroll-button to open it
$(document).bind($.browser.msie ? "mousedown" : "click", function(e)
{
    if (e.which == 2 && e.target.tagName == 'A') 
    {
        var bIE = $.browser.msie,
            $o = $(e.target),
            oe = $o.data('events'),
            b = true,
            f = function(){};

        if (typeof oe == 'object' && oe['click']) {
            f = function(){ $o.trigger('click'); }
        } else {
            b = (typeof $o[0].href == 'string' && ($o[0].target == undefined || $o[0].target == '' || $o[0].target == '_self'));
            if (b) { 
                f = function () { window.location.href=$o[0].href; };
            }
        }

        if (!b) { return; }

        e.stopImmediatePropagation();
        e.stopPropagation();
        e.preventDefault();

        if (bIE)
        {
            if (!window.__swcp__) { 
                window.__swcp__= $('<div style="position:fixed;top:0px;left:0px;width:100%;height:100%;background:#000;display:block;z-index:9999;opacity:0.01;filter:alpha(opacity:1);" />').appendTo('body'); 
            }
            window.__swcp__.show();
        }

        setTimeout(f, 50);
        if (bIE) { 
            setTimeout( function() { window.__swcp__.hide(); }, 1000 );
        }

        return false;
    }
});

中间按钮现在像鼠标左键一样工作。正如您所看到的,IE还需要更多的操作,我们必须使链接标签在1秒钟内失焦,以避免它被打开在额外的选项卡中。我使用了一个简单的方法来实现这个目的,创建一个“透明”div(不透明度为0.01),将其放置在窗口内容上方,这样页面上的任何链接都将失焦。
用户不会注意到任何区别,除了鼠标指针可能会从指针更改为默认值,一秒钟后再次更改为指针(仅适用于IE)。我可以接受这个答案,但如果您有更好的想法,请让我知道。

1
请注意,jquery.browser已经被弃用。 http://api.jquery.com/jQuery.browser/ - Frisbetarian-Support Palestine
Frisberarian < 感谢提供的额外信息。当IE9首次推出时,我发现使用高于v1.5.1版本的jQuery存在一些问题。这就是为什么我今天仍然使用稍微修改过的v1.5.1版本,在所有现代浏览器(包括旧版和移动版)中都能正常工作。此外,一些旧代码和插件无法与新版本的jQuery一起使用,因为有太多的更改(=太多额外的工作),我将继续使用v1.5.1直到真正需要更改为止。 - Codebeat
抱歉如果我的编辑看起来有点激进,但是它真的很难理解。我试图保留变量不变,但它们确实应该被重命名,而且我认为一些重构,比如重用浏览器检查,可能是一个好主意。 - Joel Peltonen
谢谢Nenotiep。有些东西是你喜欢的风格。我不喜欢赞美式样,因为它们很难阅读,因为你不能直接看到语句,从来没有理解为什么它们不对齐。变量,我知道,但JavaScript必须紧凑以提高速度。现在我使用Google封装编译器来缩小脚本。此外,重要的是要看代码中发生了什么,而不是看变量名。当然,清晰的变量名可以澄清事情,因为JavaScript没有声明类型,它可以是任何东西。不要过多依赖变量名。 - Codebeat
当你能够读懂这段代码时,你几乎可以读懂任何代码。我通常使用以变量期望类型(字母)开头的变量名。例如:o表示对象,$表示jQuery对象,b表示布尔值,s表示字符串,i表示整数,e表示事件,f表示函数等等。 - Codebeat
当你能够阅读这个时,你几乎可以阅读任何东西。我总是使用以变量的预期类型(字母)开头的变量名。o = 对象,$ = jQuery对象,b = 布尔值,s = 字符串,i = 整数,e = 事件,f = 函数等等。我敢打赌,现在你知道了这些,你可以更容易地阅读它。对于预期类型的提示对我来说比一个漂亮的变量名更有用,只要函数名足够清晰即可。jQuery的强大之处在于紧凑性。如果你无法处理它,请使用另一个框架库。 - Codebeat

1
使用HTML5的正确方法是使用auxclick事件。例如,要完全禁用元素上的中间鼠标按钮单击:
    $element.on("auxclick.disablemiddleclick", function(e)
    {
        // https://developer.mozilla.org/en-US/docs/Web/API/Element/auxclick_event
        if (e.which === 2 && e.cancelable) // disable middle button click
        {
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            console.log("disabled middle click, e=", e);
        }
    });

“auxclick”处理程序名称的后缀旨在允许使用$element.off("auxclick.disablemiddleclick")轻松删除此处理程序,而无需跟踪函数引用。”

现在这是一个有效的答案(看一下支持版本号),但是这个问题已经存在将近10年了!例如,10年前IE并不支持这个。 - Codebeat
是的,MSIE和HTML5之间唯一的联系就是“MSIE不支持HTML5”这句话。 - Mikko Rantalainen

-1

e.which应该可以完成它:

简单的例子:

$(document).ready(function(){
    $(document).mousedown(function(e){
         alert(e.which); // ##=> left
    }) 
})

5
这不是问题的答案,也根本不是任何答案。 - Codebeat
不,它并没有 - 问题在于浏览器处理中间点击时,即使链接被中间点击,它仍然会打开一个新的选项卡/窗口。请参见我在上面评论中的 fiddle。 - no.good.at.coding

-3

如果你(像我一样)因为客户端的奇怪中键单击错误而找到了这篇文章。而且你只想让任何中键或右键行为像左键一样

忘记所有那些针对IE的无聊代码。

简短而精炼:

$(document).click(function(e){
  // checking for any non left click and convert to left click.
  if (e.which != 1) { 
    e.which = 1;
  }
});

显然,如果您需要更精细的区分,可以做如下操作:

$('a.no-non-left-clickies').click(function(e){
  // checking for any non left click and convert to left click.
  if (e.which != 1) { 
    e.which = 1;
  }
});

好的,我仔细看了一下你最初的问题,我发现你确实需要比我提到的更多的IE垃圾,但是,我的陈述仍然成立,总的来说。如果你想快速将中间或右键转换为左键,上面的简单代码将在我测试过的所有浏览器上工作。 - Drupal Development Costa Rica
我还没有尝试过你的解决方案,但它是否也避免了打开新页面的情况? - Codebeat

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