Why does middle-click not trigger 'click' in several cases?
Asked Answered
S

3

14

Here's a JSFiddle of the behavior I'm seeing, relating to middle-click and the click event in Chrome and FF.

'click' kinda sorta works

Approach 1: Bind a click handler directly to an a element and a middle-click will trigger the handler in Chrome but not in FF.

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

Approach 2: Bind a delegated click handler to a div which contains one or more a. Middle click will not trigger this handler in Chrome or FF.

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

This approach is extremely valuable if the div starts out empty and the a elements are filled in later by an AJAX call, or as a result of some user input.

'mouseup' works

Using mouseup instead of click causes both approach 1 and 2 to work in both browsers.

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

Here's the JSFiddle with mouseup.

This is interesting and might be useful in some cases, because mouseup is almost click. But mouseup isn't click, and I'm after the behavior of click. I do not want to create a hacky mousedown; setTimeout; mouseup simulation of click.

I'm pretty sure the answer is "nope", but is there a cross-browser way to cause middle-click to trigger click handlers? If not, what are the reasons why?

Sassaby answered 22/11, 2013 at 21:34 Comment(0)
R
11

The click event is generally fired for the left mouse button, however, depending on the browser, the click event may or may not occur for the right and/or middle button.

In Internet Explorer and Firefox the click event is not fired for the right or middle buttons.

Therefore, we cannot reliably use the click event for event handlers on the middle or right button.

Instead, to distinguish between the mouse buttons we have to use the mousedown and mouseup events as most browsers do fire mousedown and mouseup events for any mouse button.

in Firefox and Chrome event.which should contain a number indicating what mouse button was pressed (1 is left, 2 is middle, 3 is right).

In Internet Explorer on the other hand, event.button indicates what mouse button was clicked (1 is left, 4 is middle, 2 is right);

event.button should also work in Firefox and other browsers, but the numbers can be slightly different (0 is left, 1 is middle, 2 is right).

So to put that together we usually do something like this :

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
        }
    }
}

As jQuery normalizes event.which, you should only have to use that in jQuery event handlers, and as such be doing:

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

In other words you can't use the onclick event, so to simulate it you can use both mousedown and mouseup.

You can add a timer to limit the time allowed between the mousedown and mouseup event, or even throw in a mousemove handler to limit the movement between a mousedown and mouseup event, and make the event handler not fire if the mouse pointer moved more than ten pixels etc. the possibilites are almost endless, so that shouldn't really be an issue.

$('#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);
        }
    }
});
Ruminant answered 22/11, 2013 at 22:14 Comment(4)
I appreciate your answer, however I did state in the question "I do not want to create a hacky mousedown; setTimeout; mouseup simulation of click." Reinventing the click seems like a bad idea, but I'll weigh the pros and cons again.Sassaby
And I stated in my answer that it is the only reliable way to capture a click on the middle button, there are no alternatives.Ruminant
Is it necessary to use the === comparation instead of ==? Why? Thanks.Anaphora
@ElTête - It's not neccessary, but as e.which always returns a number, why not just compare strict against a number.Ruminant
J
3

Short answer: Nope.

The question is, what do you want to capture the middle clicks for? A middle click isn't meant to interact with the current page but rather to open a link in a new tab.

Chrome is also currently working on droping this behavior: https://code.google.com/p/chromium/issues/detail?id=255

And there is currently a general discussion on the w3c mailing list about this topic: http://lists.w3.org/Archives/Public/www-dom/2013JulSep/0203.html


Yet for now, you can catch middleclicks in Firefox on a document-level:

$(document).on('click', function(e){
    console.log(e);
});
Judicator answered 22/11, 2013 at 22:3 Comment(3)
Excellent, thanks for the background info. The use case is capturing analytics data for middle-clicks (just like ctrl+clicks). Ctrl+click does fire onclick handlers.Sassaby
The Chromium issue 255, comment 160 talks about a new event auxclick, and this request in caniuse website includes even more useful links.Pervious
Hi @BojidarStanchev , my intention was not to be condescending or arrogant in any way – I surely don't know every use case. I should have written this question as a comment to the original question and not as part of an answer. Valid point and thanks for the feedback. At the same time, please note that the question and answer are almost seven years old – hence I will not update it.Judicator
L
0

I've build a factory for creating Middle mouse click handlers using vanilla JS and working in latest Firefox and 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)
};
Lauritz answered 19/4, 2018 at 7:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.