72

I tried to detect which mouse button -if any- is the user pressing during a mousemove event under jQuery, but I'm getting ambiguous results:

no button pressed:      e.which = 1   e.button = 0
left button pressed:    e.which = 1   e.button = 0
middle button pressed:  e.which = 2   e.button = 1
right button pressed:   e.which = 3   e.button = 2

Code:

<!DOCTYPE html>
<html>
<head>
  <script src="http://code.jquery.com/jquery-latest.min.js"></script>
</head>
<body>

<input id="whichkey" value="type something">
<div id="log"></div>
<script>$('#whichkey').bind('mousemove',function(e){ 
  $('#log').html(e.which + ' : ' +  e.button );
});  </script>

</body>
</html>

How can I tell the difference between left mouse button pressed and no button at all?

Sebastián Grignoli
  • 32,444
  • 17
  • 71
  • 86
  • Please explain what you are trying to do better. Should mouseenter then mousedown then mouseup then mouseexit result in no button? What about mousedown then mouseenter then mouseexit then mouseup? Are you asking that if the mousemove s across the input, the fact that no button was pressed is recorded? – Fresheyeball May 28 '11 at 02:06
  • I'm trying to highlight text in the page. Each word is in it's own span. Mouseover+left button over a span element should highlight it, mouseover+right button should unhighlight it. – Sebastián Grignoli May 31 '11 at 16:44

7 Answers7

64

You can write a bit of code to keep track of the state of the left mouse button, and with a little function you can pre-process the event variable in the mousemove event.

To keep track of the state of the LMB, bind an event to document level for mousedown and mouseup and check for e.which to set or clear the flag.

The pre-processing is done by the tweakMouseMoveEvent() function in my code. To support IE versions < 9, you have to check if mouse buttons were released outside the window and clear the flag if so. Then you can change the passed event variable. If e.which was originally 1 (no button or LMB) and the current state of the left button is not pressed, just set e.which to 0, and use that in the rest of your mousemove event to check for no buttons pressed.

The mousemove handler in my example just calls the tweak function passing the current event variable through, then outputs the value of e.which.

$(function() {
    var leftButtonDown = false;
    $(document).mousedown(function(e){
        // Left mouse button was pressed, set flag
        if(e.which === 1) leftButtonDown = true;
    });
    $(document).mouseup(function(e){
        // Left mouse button was released, clear flag
        if(e.which === 1) leftButtonDown = false;
    });

    function tweakMouseMoveEvent(e){
        // Check from jQuery UI for IE versions < 9
        if ($.browser.msie && !e.button && !(document.documentMode >= 9)) {
            leftButtonDown = false;
        }

        // If left button is not set, set which to 0
        // This indicates no buttons pressed
        if(e.which === 1 && !leftButtonDown) e.which = 0;
    }

    $(document).mousemove(function(e) {
        // Call the tweak function to check for LMB and set correct e.which
        tweakMouseMoveEvent(e);

        $('body').text('which: ' + e.which);
    });
});

Try a live demo here: http://jsfiddle.net/G5Xr2/

lex82
  • 11,173
  • 2
  • 44
  • 69
DarthJDG
  • 16,511
  • 11
  • 49
  • 56
  • Thanks, I couldn't figure out why which was always 0 in IE. – Jethro Larson Dec 13 '11 at 01:16
  • nice solution. what about if there is a mousedown event on a child element ? will the the document mousedown event still be fired ? – Steve B Apr 13 '12 at 11:50
  • Keep in mind that mouseup event doesn't fire on most browsers when mousedown was triggered on a scrollbar. – Stas Bichenko Nov 14 '12 at 09:31
  • For me it doesn't work in IE9 (standart mode if it matters) I've changed >=9 to >9 to make it working. But what about IE10? – IvanH Nov 16 '12 at 13:06
  • 5
    mouseup will not fire if you release the button when not on the browser. – Ariel Jun 13 '13 at 11:06
  • 3
    **WARNING:** `$.browser` is deprecated and will break with jQuery 1.9+. You'll need to use the [jquery migrate plugin](https://github.com/jquery/jquery-migrate/) to get it working again. See: http://stackoverflow.com/q/9638247/165673 – Yarin Jul 22 '13 at 13:54
  • 4
    Note that it's always best to attach the `mousedown` and `mouseup` listeners to `document`, and not necessarily the same element you're attaching the `mousemove` handler to. This is to handle cases like where the mouse is dragged off the edge of the element. But this assumes the event always propagates, and as @Ariel notes, it doesn't handle the mouse being released outside the browser! I continue to look for an accurate mouse button state, just like a keyboard modifier state, on a `mousemove` event, no matter what the mouse went down on. (Did I really just type that?) – Michael Scheper Nov 13 '14 at 01:50
  • Michael Scheper, I have been struggling with the same thing, and it appears that as of 2015 there is no _reliable_ cross-browser way of detecting whether the mouse button is being pressed at all during a mousemove! The best you can do override Event.prototype.preventDefault and add the code there yourself, for mouseup and touchend events, before proxying the call to the real Event.prototype.preventDefault! – Gregory Magarshak Sep 18 '15 at 22:19
  • err sorry I meant stopPropagation() – Gregory Magarshak Sep 18 '15 at 22:33
  • For me, I really just wanted to know when the mouse was released. As long as your handler is on the document and not 'body', this works in Chrome and Firefox. I don't have a solution for IE though. – frodo2975 Aug 12 '16 at 19:51
  • Actually, detecting mouseups outside the window works for me even in IE11. It just doesn't work in the fiddle for some reason, perhaps because it's in an iframe – frodo2975 Aug 12 '16 at 20:14
26

Most browsers (except Safari) *) now support the MouseEvent.buttons property (note: plural "buttons"), which is 1 when the left mouse button is pressed:

$('#whichkey').bind('mousemove', function(e) { 
    $('#log').html('Pressed: ' + e.buttons);
});

https://jsfiddle.net/pdz2nzon/2

*) The world is moving forward:
https://webkit.org/blog/8016/release-notes-for-safari-technology-preview-43/
https://trac.webkit.org/changeset/223264/webkit/
https://bugs.webkit.org/show_bug.cgi?id=178214

Sphinxxx
  • 12,484
  • 4
  • 54
  • 84
  • 4
    This answer should be the accepted one! Browsers from lazy vendors could get help from: https://github.com/mikolalysenko/mouse-event – yckart May 01 '16 at 11:56
  • 1
    Came to add this answer, cuz others don't work. Agree this should be accepted answer. – BoB3K May 25 '16 at 18:45
  • 1
    I am using the latest Firefox and in the "PointerEvent" handlers which supposedly handle the event.buttons property for the mouse device, the data is NOT ACCURATE. I wonder how complicated can it be to provide these flags accurately? I do it fine on native platforms in C++. – peterk Feb 07 '19 at 22:18
6

jQuery normalizes the which value so it will work across all browsers. I bet if you run your script you will find different e.button values.

Mottie
  • 84,355
  • 30
  • 126
  • 241
  • That's what I thought. Sadly, `which` does not tell me if the left button is pressed or not. – Sebastián Grignoli Nov 01 '10 at 01:19
  • I think some events don't process button data as others, try adding a global variable and changing its value with a `mousedown` event. – Mottie Nov 01 '10 at 01:30
  • You are right. Answer that in a separate answer if you want, so I can mark it as correct. – Sebastián Grignoli Nov 01 '10 at 01:41
  • 6
    You will not get mousedown event if it happens outside browser window, so a global variable could be put into inconsistent state by a) pressing mouse button, b) moving mouse outside the browser window, c) releasing mouse button, d) moving mouse back into the window - so it's not a solution – sereda Nov 08 '10 at 10:20
  • @sereda, you are right. I was testing on Firefox and Chrome and the problem you describe does not occur. Now I tested on IE, and found the undesired behavior you described. What do you suggest? – Sebastián Grignoli Nov 09 '10 at 22:14
  • jQuery's normalization-formula: `( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) )` – yckart May 01 '16 at 11:59
6

For some reason, when binding to mousemove event, the event.which property is set to 1 if left button or none is pressed.

I changed to mousedown event and it worked Ok:

  • left: 1
  • middle: 2
  • right: 3

Here's a working example: http://jsfiddle.net/marcosfromero/RrXbX/

The jQuery code is:

$('p').mousedown(function(event) {
    console.log(event.which);
    $('#button').text(event.which);
});
marcosfromero
  • 2,853
  • 17
  • 17
2

Those variables are updated when the mousedown event (amongst others) fires; you're seeing the value that remains from that event. Note that they are properties of that event, not of the mouse itself:

Description: For key or button events, this attribute indicates the specific button or key that was pressed.

There is no value for "no button press", because the mousedown event will never fire to indicate that there's no button being pressed.

You'll have to keep your own global [boolean] variable and toggle it on/off on mousedown/mouseup.

  • This will only work if the browser window had focus when the button was pressed, which means a focus-switch between the user depressing the mouse button and your mousemove event firing will prevent this from working properly. However, there's really not much you can do about that.
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
1

As of the date, the following works on Firefox 47, Chrome 54 and Safari 10.

$('#whichkey').bind('mousemove',function(e){
    if (e.buttons & 1 || (e.buttons === undefined && e.which == 1)) {
        $('#log').html('left button pressed');
    } else if (e.buttons & 2 || (e.buttons === undefined && e.which == 3)) {
        $('#log').html('right button pressed');
    } else {
        $('#log').html('no button pressed');
    }
});
Giancarlo Sportelli
  • 1,219
  • 1
  • 17
  • 20
0

For users looking for similar solution but without the use of JQuery, here is a way of how I solved my problem:

    canvas.addEventListener("mousemove", updatePosition_mouseNtouch);

    function updatePosition_mouseNtouch (evt) {
      // IF mouse is down THEN set button press flag
      if(evt.which === 1)
        leftButtonDown = true;
      // If you need to detect other buttons, then make this here
      // ... else if(evt.which === 2) middleButtonDown = true;
      // ... else if(evt.which === 3) rightButtonDown = true;
      // IF no mouse button is pressed THEN reset press flag(s)
      else
        leftButtonDown = false;

      if (leftButtonDown) {
        /* do some stuff */
      }
    }

I hope this is usefull to someone seeking an answer.