JavaScript: Check if mouse button down?
Asked Answered
C

17

181

Is there a way to detect if a mouse button is currently down in JavaScript?

I know about the "mousedown" event, but that's not what I need. Some time AFTER the mouse button is pressed, I want to be able to detect if it is still pressed down.

Is this possible?

Cuticle answered 26/11, 2008 at 22:29 Comment(7)
Why you accept an Answer that does not answers your question. Registering mousedown and mouseup is wrong (as you can mouseup outside the browser and let it think you're still dragging). Coming after your problem and reaching this page is completely uselessJuba
@Juba it does answer my question. Please read my question and then read the answer. Apparently over 100 other people agree that it was a good answer.Cuticle
yea.. but me, alfred, and other 8 are telling you that tracking mouseup and mousedown is not the right way. Correct answer should involve analysis of event object in the handler of your choice, or analysis of all mouse related events (like enter/exit) if you need time-driven check instead of a event-driven check.Juba
This question was asked and answered 7+ years ago. I don't get a notification when there are new answers (but I do for comments). Write a better answer and the community will upvote it to the top :) Lots of information gets stale and things change.Cuticle
@Cuticle Sorry to necro, but... No, that info will never get to the top. A non-selfanswer, once accepted, sticks at the top forever. Also, "oh so many people voted for this one" doesn't mean the answer is a good one; it just means lots of people saw it, thought it looked alright, and hit the upvote because it seemed to work for them.Crepe
Possible duplicate of Check if mouse button is down while hovering?Rhines
@Nic - SE changed that, now the accepted answer isn't necessarily at the top. If you haven't already upvoted Jono Jobs' answer, you might consider it so it eventually rises the top.Wheeze
H
169

Regarding Pax' solution: it doesn't work if user clicks more than one button intentionally or accidentally. Don't ask me how I know :-(.

The correct code should be like that:

var mouseDown = 0;
document.body.onmousedown = function() { 
  ++mouseDown;
}
document.body.onmouseup = function() {
  --mouseDown;
}

With the test like this:

if(mouseDown){
  // crikey! isn't she a beauty?
}

If you want to know what button is pressed, be prepared to make mouseDown an array of counters and count them separately for separate buttons:

// let's pretend that a mouse doesn't have more than 9 buttons
var mouseDown = [0, 0, 0, 0, 0, 0, 0, 0, 0],
    mouseDownCount = 0;
document.body.onmousedown = function(evt) { 
  ++mouseDown[evt.button];
  ++mouseDownCount;
}
document.body.onmouseup = function(evt) {
  --mouseDown[evt.button];
  --mouseDownCount;
}

Now you can check what buttons were pressed exactly:

if(mouseDownCount){
  // alright, let's lift the little bugger up!
  for(var i = 0; i < mouseDown.length; ++i){
    if(mouseDown[i]){
      // we found it right there!
    }
  }
}

Now be warned that the code above would work only for standard-compliant browsers that pass you a button number starting from 0 and up. IE uses a bit mask of currently pressed buttons:

  • 0 for "nothing is pressed"
  • 1 for left
  • 2 for right
  • 4 for middle
  • and any combination of above, e.g., 5 for left + middle

So adjust your code accordingly! I leave it as an exercise.

And remember: IE uses a global event object called … "event".

Incidentally IE has a feature useful in your case: when other browsers send "button" only for mouse button events (onclick, onmousedown, and onmouseup), IE sends it with onmousemove too. So you can start listening for onmousemove when you need to know the button state, and check for evt.button as soon as you got it — now you know what mouse buttons were pressed:

// for IE only!
document.body.onmousemove = function(){
  if(event.button){
    // aha! we caught a feisty little sheila!
  }
};

Of course you get nothing if she plays dead and not moving.

Relevant links:

Update #1: I don't know why I carried over the document.body-style of code. It will be better to attach event handlers directly to the document.

Halfbound answered 27/11, 2008 at 2:53 Comment(17)
Great post, thanks! The last bit about catching the button onmousemove is perfect, since I only need this info to resolve a problem that happens in IE only (certain scriptaculous callbacks fire while dragging, when they should only fire when dragging stops, and I needed a way to stop them)Cuticle
After testing, it appears that evt.button does in fact not exist onmousemove with IE7...Cuticle
It always pays to qualify your question as precisely as possible. I just added the last bit as the fun fact. Now I am glad I did it because it turned out to be useful for you.Halfbound
Hmm, the documentation says it is supported from IE4 to IE8.Halfbound
document.body.someevent doesn't work in IE7. With the removal of the "event" parameter, event.button does work correctly. Sadly, this doesn't quite work for my workaround, since on at least one of the many firings of "onmousemove", it slips through as a zero. Thanks for the information anyway.Cuticle
I think this won't work in Chrome, because Chrome doesn't generate mouseup events if the original mousedown happened on a scrollbar. An example, from this Chrome/Chromium issue. So your left mouse button would be down forever, if there's a scrollbar that someone uses! (In Chrome)Maye
I love your solution, but what if the mouse is coming from a different application say ms word and its dragging some text. How will you detect that the mouse is down?Waitress
@bogonko --- there is no portable way to do it. But: google for drag and drop API for browsers (not all browsers support it, supporting browsers can do it differently). In any case, if a browser doesn't work up to your expectations do complain to its vendor --- we have to be heard.Halfbound
@Maye is correct about the mouseup event not being fired if the mousedown originated on a scrollbar. This bug in fact seems to be present in all browsers except firefox! See #4145902Stine
I'm not understanding why you're incrementing the mousedown array elts in the second example. Why a counter for each button rather than a bool for each button?Millennial
One gets simply amazed when learning about these kind of events after many years of experience.Illene
This is a really popular question (and answer). I'm surprised this hasn't come up yet, but you don't need to store the mouse data at all. You can just plug event.buttons into whatever event trigger you're using, without external saved variables. I asked a brand new question (because my mouseup event doesn't always fire, so this solution doesn't work for me) and got that answer in maybe 5 minutes tops.Drench
Regarding incrementing vs. using bools: the code is shorter with incrementing/decrementing. At the end of the day it is a matter of taste.Halfbound
Even in 2018, this thread is still relevant. I haven't verified the scroll issue, but my own application (drawing app) becomes stuck in 'mousedown' when dragging highlighted text across the drawing canvas.Pecoraro
I feel like this answer has a rather large problem. if you click down and then release outside the window, or right click at all, mouseup will not be triggered, your value will be stuck greater than 0 and your check will permanently fail.Inconsiderable
There is no problem with clicking down and then releasing outside the window. The only problem I know of with this mechanism, when a mouse down is converted into a text copy operation. If it is critical, you should guard against it either with CSS, or JavaScript, whatever is appropriate.Halfbound
@Drench The event.buttons property was not supported in all browsers, though (and it still isn't). MDN referencePlenipotentiary
H
83

This is an old question, and the answers here seem to mostly advocate for using mousedown and mouseup to keep track of whether a button is pressed. But as others have pointed out, mouseup will only fire when performed within the browser, which can lead to losing track of the button state.

However, MouseEvent (now) indicates which buttons are currently pushed:

  • For all modern browsers (including Safari v11.1+ [v11.3+ on iOS]), use MouseEvent.buttons
  • For Safari < 11.1 (11.3 on iOS), use MouseEvent.which (buttons will be undefined for Safari) Note: which uses different numbers from buttons for Right and Middle clicks.

When registered on document, mousemove will fire immediately as soon as the cursor reenters the browser, so if the user releases outside then the state will be updated as soon as they mouse back inside.

A simple implementation might look like:

var primaryMouseButtonDown = false;

function setPrimaryButtonState(e) {
  var flags = e.buttons !== undefined ? e.buttons : e.which;
  primaryMouseButtonDown = (flags & 1) === 1;
}

document.addEventListener("mousedown", setPrimaryButtonState);
document.addEventListener("mousemove", setPrimaryButtonState);
document.addEventListener("mouseup", setPrimaryButtonState);

That code tracks the state of the primary mouse button (typically the left), ignoring the state of other mouse buttons.

If more complicated scenarios are required (different buttons/multiple buttons/control keys), check out the MouseEvent docs.

Haha answered 25/2, 2018 at 5:58 Comment(6)
I have a situation where I'm expecting that during a mousemove I should be getting the buttons code to be '1' but it produces '7', - all work is on Chrome. Someone have any idea?Pressroom
@VasilyHall Looking at the MDN docs for MouseEvent.buttons, it would seem that 7 means that it thinks that the Primary, Secondary and Auxilary buttons are all being pressed together. I'm not sure why this would be the case - are you using an advanced mouse? If so, perhaps it's worth trying with a basic one. If you're just needing to get it going, you could use a bitwise check to ensure that the left button is pressed and ignore any others. eg. MouseEvent.buttons & 1 === 1.Haha
Thank you, I was using my JavaScript inside of a special browser: the embedded Chrome(ium?) within the Adobe Illustrator application- I bet the combination of all the various things somehow leads to a 7, although just the one button is being pressed. Seeing as how this is consistent within my application, I'm simply doing a check for 1 or 7 to facilitate my interactivity. I will add, although I do use Logitech M570 (or whatever) trackball mouse, it correctly registers the 1 in regular browser but the 7 inside of Illustrator.Pressroom
Another example of why it is better to read Stack Overflow pages from the bottom up.Zack
Why write shorthandd in an answer to a question? I'm thinking you would want to keep it as simple and intuitive as possible, this just makes new coders focus on understanding the syntax rather than the actual sollution.Airla
Also, .which was allready depracted when this was written, and Safari does now at least support .buttons.Airla
R
22

I think the best approach to this is to keep your own record of the mouse button state, as follows:

var mouseDown = 0;
document.body.onmousedown = function() { 
    mouseDown = 1;
}
document.body.onmouseup = function() {
    mouseDown = 0;
}

and then, later in your code:

if (mouseDown == 1) {
    // the mouse is down, do what you have to do.
}
Rajah answered 27/11, 2008 at 0:48 Comment(4)
+1, and thanks. I thought I might have to resort to this, but I was hoping there was some feature that let you simply check the state directly.Cuticle
Is there a reason for not using a boolean?Texture
@Texture I think Int appears to be less typing, and ultimately slightly better performance: jsperf.com/bool-vs-intBizerte
Maybe he's actually a MySQL DBA ;)Bonkers
W
14

the solution isn't good. one could "mousedown" on the document, then "mouseup" outside the browser, and on this case the browser would still be thinking the mouse is down.

the only good solution is using IE.event object.

Wrens answered 26/12, 2010 at 8:14 Comment(2)
I see where you are coming from, but having a solution that only works for IE isn't a good solution either.Cuticle
@Wrens In some case it might help to know if mousemove is out of the window. If(event.target.nodeName.toLowerCase==='html')down=0;Eliaseliason
T
7

I know this is an old post, but I thought the tracking of mouse button using mouse up/down felt a bit clunky, so I found an alternative that may appeal to some.

<style>
    div.myDiv:active {
        cursor: default;
    }
</style>

<script>
    function handleMove( div ) {
        var style = getComputedStyle( div );
        if (style.getPropertyValue('cursor') == 'default')
        {
            // You're down and moving here!
        }
    }
</script>

<div class='myDiv' onmousemove='handleMove(this);'>Click and drag me!</div>

The :active selector handles the mouse click much better than mouse up/down, you just need a way of reading that state in the onmousemove event. For that I needed to cheat and relied on the fact that the default cursor is "auto" and I just change it to "default", which is what auto selects by default.

You can use anything in the object that is returned by getComputedStyle that you can use as a flag without upsetting the look of your page e.g. border-color.

I would have liked to set my own user defined style in the :active section, but I couldn't get that to work. It would be better if it's possible.

Talkington answered 4/2, 2016 at 23:32 Comment(1)
Thanks for sharing this. I was using this idea for a drag-n-drop styling logic , but faced an issue on IE and had to include additional logicInn
L
6

If you're working within a complex page with existing mouse event handlers, I'd recommend handling the event on capture (instead of bubble). To do this, just set the 3rd parameter of addEventListener to true.

Additionally, you may want to check for event.which to ensure you're handling actual user interaction and not mouse events, e.g. elem.dispatchEvent(new Event('mousedown')).

var isMouseDown = false;

document.addEventListener('mousedown', function(event) { 
    if ( event.which ) isMouseDown = true;
}, true);

document.addEventListener('mouseup', function(event) { 
    if ( event.which ) isMouseDown = false;
}, true);

Add the handler to document (or window) instead of document.body is important b/c it ensures that mouseup events outside of the window are still recorded.

Langford answered 21/12, 2017 at 18:47 Comment(0)
G
5

Using the MouseEvent api, to check the pressed button, if any:

// Mouse buttons
document.addEventListener('mousedown', e => console.log(e.buttons))
// Keyboard keys
document.addEventListener('keydown', e => console.log(e.key))

Return:

A number representing one or more buttons. For more than one button pressed simultaneously, the values are combined (e.g., 3 is primary + secondary).

0 : No button or un-initialized
1 : Primary button (usually the left button)
2 : Secondary button (usually the right button)
4 : Auxilary button (usually the mouse wheel button or middle button)
8 : 4th button (typically the "Browser Back" button)
16 : 5th button (typically the "Browser Forward" button)
Gilli answered 24/7, 2019 at 10:47 Comment(1)
The question asked how to not use mousedownPhipps
W
5

In case someone else runs into this, you can use .matches with the :active selector:

function mouseDown() {
    return document.body.matches(":active");
}
Woodborer answered 24/5, 2021 at 18:23 Comment(3)
One caveat: This didn't work for me on an Android tablet (and I suspect wouldn't work on an Android phone if I had one to test with now.) I'm still using this trick to help make sure a drag action ends even if I miss the mouseup/touchend event, but I need to check that document.body.matches(":active") is true when a drag action starts as well, and only then use this as an extra test for the end of the drag.Slavic
It is parsing the whole DOM at any clicks.Gilli
@Slavic yes apparently it doesn't work for mobile and it doesn't work for the mobile devtools thingamajig eitherWoodborer
F
4

The following snippet will attempt to execute the "doStuff" function 2 seconds after the mouseDown event occurs in document.body. If the user lifts up the button, the mouseUp event will occur and cancel the delayed execution.

I'd advise using some method for cross-browser event attachment - setting the mousedown and mouseup properties explicitly was done to simplify the example.

function doStuff() {
  // does something when mouse is down in body for longer than 2 seconds
}

var mousedownTimeout;

document.body.onmousedown = function() { 
  mousedownTimeout = window.setTimeout(doStuff, 2000);
}

document.body.onmouseup = function() {
  window.clearTimeout(mousedownTimeout);
}
Fixative answered 27/11, 2008 at 0:34 Comment(1)
Thanks, but this isn't quite the behavior I was looking for (although I can see how my question indicates that this is what I am trying to do).Cuticle
F
3

You can combine @Pax and my answers to also get the duration that the mouse has been down for:

var mousedownTimeout,
    mousedown = 0;

document.body.onmousedown = function() {
  mousedown = 0; 
  window.clearInterval(mousedownTimeout);
  mousedownTimeout = window.setInterval(function() { mousedown += 200 }, 200);
}

document.body.onmouseup = function() {
  mousedown = 0;
  window.clearInterval(mousedownTimeout);
}

Then later:

if (mousedown >= 2000) {
  // do something if the mousebutton has been down for at least 2 seconds
}
Fixative answered 27/11, 2008 at 0:54 Comment(2)
That's not a bad idea, Jake. Do you need to clearInterval() in onmouseup() before setting mousedown to 0? I'm wary of the timer function firing between setting it to 0 and stopping the timer, leaving the timeout at 200? Similarly in onmousedown.Rajah
The order of the clearIntervals isn't going to make a difference as the current thread of execution will finish before any events (including timers) fire.Recluse
B
2

You need to handle the MouseDown and MouseUp and set some flag or something to track it "later down the road"... :(

Bunder answered 29/11, 2008 at 22:29 Comment(0)
S
2

Short and sweet

I'm not sure why none of the previous answers worked for me, but I came up with this solution during a eureka moment. It not only works, but it is also most elegant:

Add to body tag:

onmouseup="down=0;" onmousedown="down=1;"

Then test and execute myfunction() if down equals 1:

onmousemove="if (down==1) myfunction();"
Soilure answered 12/8, 2012 at 14:7 Comment(2)
you are new, so I am going to help you out a bit, here. First off, mind your grammar in your posts - I probably spent more time fixing it than you put into it. Second, your solution is just another version of the others listed above - it is nothing new. Third, your solution uses a technique referred to as "using flags", which Thomas Hansen suggested, above. Please take care to check your grammar and to not repeat solutions when posting to older posts without providing some explanation regarding why the other solutions did not work for you.Recuperative
I did, however vote your solution up, as it IS valid and you are new to SORecuperative
I
2

Using jQuery, the following solution handles even the "drag off the page then release case".

$(document).mousedown(function(e) {
    mouseDown = true;
}).mouseup(function(e) {
    mouseDown = false;
}).mouseleave(function(e) {
    mouseDown = false;
});

I don't know how it handles multiple mouse buttons. If there were a way to start the click outside the window, then bring the mouse into the window, then this would probably not work properly there either.

Incuse answered 4/1, 2014 at 20:12 Comment(0)
T
1

As said @Jack, when mouseup happens outside of browser window, we are not aware of it...

This code (almost) worked for me:

window.addEventListener('mouseup', mouseUpHandler, false);
window.addEventListener('mousedown', mouseDownHandler, false);

Unfortunately, I won't get the mouseup event in one of those cases:

  • user simultaneously presses a keyboard key and a mouse button, releases mouse button outside of browser window then releases key.
  • user presses two mouse buttons simultaneously, releases one mouse button then the other one, both outside of browser window.
Troupe answered 20/9, 2017 at 8:14 Comment(0)
L
0
        var mousedown = 0;
        $(function(){
            document.onmousedown = function(e){
                mousedown = mousedown | getWindowStyleButton(e);
                e = e || window.event;
                console.log("Button: " + e.button + " Which: " + e.which + " MouseDown: " + mousedown);
            }

            document.onmouseup = function(e){
                mousedown = mousedown ^ getWindowStyleButton(e);
                e = e || window.event;
                console.log("Button: " + e.button + " Which: " + e.which + " MouseDown: " + mousedown);
            }

            document.oncontextmenu = function(e){
                // to suppress oncontextmenu because it blocks
                // a mouseup when two buttons are pressed and 
                // the right-mouse button is released before
                // the other button.
                return false;
            }
        });

        function getWindowStyleButton(e){
            var button = 0;
                if (e) {
                    if (e.button === 0) button = 1;
                    else if (e.button === 1) button = 4;
                    else if (e.button === 2) button = 2;  
                }else if (window.event){
                    button = window.event.button;
                }
            return button;
        }

this cross-browser version works fine for me.

Leggett answered 4/7, 2013 at 14:49 Comment(0)
P
0

Below jQuery example, when mouse is over $('.element'), color is changing depending on which mouse button is pressed.

var clicableArea = {
    init: function () {
        var self = this;
        ('.element').mouseover(function (e) {
            self.handlemouseClick(e, $(this));
        }).mousedown(function (e) {
            self.handlemouseClick(e, $(this));
        });
    },
    handlemouseClick: function (e, element) {
        if (e.buttons === 1) {//left button
            element.css('background', '#f00');
        }
        if (e.buttons === 2) { //right buttom
            element.css('background', 'none');
        }
    }
};
$(document).ready(function () {
    clicableArea.init();
});
Pulitzer answered 25/6, 2015 at 5:5 Comment(0)
M
-2

Well, you can't check if it's down after the event, but you can check if it's Up... If it's up.. it means that no longer is down :P lol

So the user presses the button down (onMouseDown event) ... and after that, you check if is up (onMouseUp). While it's not up, you can do what you need.

Myrlemyrlene answered 26/11, 2008 at 22:37 Comment(2)
Isn't onMouseUp an event too? How will you check the "property" onMouseup? Or whose property, rather?Aposematic
@Rohit: you don't check property, you manage a flag indicating the event has happened... The mouse button is either up or down.Kissinger

© 2022 - 2024 — McMap. All rights reserved.