JavaScript: Verifique se o botão do mouse está pressionado?

136

Existe uma maneira de detectar se um botão do mouse está atualmente pressionado em JavaScript?

Eu sei sobre o evento "mousedown", mas não é disso que eu preciso. Depois de pressionar o botão do mouse, desejo detectar se ele ainda está pressionado.

Isso é possível?

TM.
fonte
14
Por que você aceita uma resposta que não responde à sua pergunta. O registro da ratoeira e do mouse está incorreto (como você pode passar o mouse para fora do navegador e deixá-lo pensar que ainda está arrastando). Vindo após o seu problema e chegar a esta página é completamente inútil
Jack
@ Jack, responde à minha pergunta. Por favor, leia minha pergunta e, em seguida, leia a resposta. Aparentemente, mais de 100 outras pessoas concordam que foi uma boa resposta.
TM.
4
sim .. mas eu, Alfredo, e outros 8 estamos lhe dizendo que rastrear mouseup e mousedown não é o caminho certo. A resposta correta deve envolver a análise do objeto de evento no manipulador de sua escolha ou a análise de todos os eventos relacionados ao mouse (como entrar / sair) se você precisar de uma verificação controlada pelo tempo em vez de uma verificação controlada pelo evento.
Jack
Esta pergunta foi feita e respondida há mais de 7 anos. Não recebo uma notificação quando há novas respostas (mas recebo comentários). Escreva uma resposta melhor e a comunidade a votará até o topo :) Muitas informações ficam obsoletas e as coisas mudam.
TM.
2
@TM. Desculpe necro, mas ... Não, essa informação nunca chegará ao topo. Uma pessoa que não responde, uma vez aceita, fica no topo para sempre. Além disso, "oh, tantas pessoas votaram neste" não significa que a resposta seja boa; significa apenas que muitas pessoas viram, acharam que estava tudo bem e atingiram o voto positivo porque parecia funcionar para elas.
Fund Monica's Lawsuit

Respostas:

147

Em relação à solução do Pax: não funciona se o usuário clicar em mais de um botão intencionalmente ou acidentalmente. Não me pergunte como eu sei :-(.

O código correto deve ser assim:

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

Com o teste como este:

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

Se você quiser saber qual botão foi pressionado, esteja preparado para fazer do mouseDown uma matriz de contadores e conte-os separadamente para botões separados:

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

Agora você pode verificar exatamente quais botões foram pressionados:

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

Agora, esteja avisado de que o código acima funcionaria apenas para navegadores compatíveis com o padrão que passam um número de botão a partir de 0 ou superior. O IE usa uma máscara de bit dos botões pressionados no momento:

  • 0 para "nada é pressionado"
  • 1 para a esquerda
  • 2 para a direita
  • 4 para o meio
  • e qualquer combinação dos itens acima, por exemplo, 5 para esquerda + média

Então ajuste seu código de acordo! Eu deixo isso como um exercício.

E lembre-se: o IE usa um objeto de evento global chamado… "event".

Aliás, o IE possui um recurso útil no seu caso: quando outros navegadores enviam "botão" apenas para eventos de botão do mouse (onclick, onmousedown e onmouseup), o IE o envia com onmousemove também. Assim, você pode começar a ouvir onmousemove quando precisar conhecer o estado do botão e verificar o botão evt.botton assim que o conseguir - agora você sabe quais botões do mouse foram pressionados:

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

Claro que você não ganha nada se ela fingir de morta e não se mover.

Links relevantes:

Atualização # 1 : Não sei por que carreguei o documento. Estilo do código do corpo. Será melhor anexar manipuladores de eventos diretamente ao documento.

Eugene Lazutkin
fonte
1
Após o teste, parece que o botão evt.de fato não existe de fato com o IE7 ...
TM.
5
Acho que isso não funcionará no Chrome, porque o Chrome não gera eventos de mouseup se a ratoeira original aconteceu em uma barra de rolagem. Um exemplo , desta edição do Chrome / Chromium . Portanto, o botão esquerdo do mouse ficaria pressionado para sempre, se houver uma barra de rolagem usada por alguém! (In Chrome)
KajMagnus
1
Adoro a sua solução, mas e se o mouse vier de um aplicativo diferente, diga ms word e esteja arrastando algum texto. Como você detectará que o mouse está pressionado?
Shadrack B. Orina #
6
Não estou entendendo por que você está incrementando os elts da matriz do mouse no segundo exemplo. Por que um contador para cada botão em vez de um bool para cada botão?
Amwinter 16/05
2
Esta é uma pergunta muito popular (e resposta). Estou surpreso que isso ainda não tenha surgido, mas você não precisa armazenar os dados do mouse. Você pode simplesmente conectar-se event.buttonsa qualquer gatilho de evento que estiver usando, sem variáveis ​​salvas externas. Fiz uma pergunta totalmente nova (porque meu evento de mouse nem sempre é acionado, portanto essa solução não funciona para mim) e recebi essa resposta em talvez 5 minutos no máximo.
RoboticRenaissance
32

Essa é uma pergunta antiga, e as respostas aqui parecem advogar principalmente o uso mousedowne o mouseupacompanhamento de pressionar um botão. Mas, como outros já apontaram, mouseupsó serão acionados quando executados no navegador, o que pode levar à perda do controle do estado do botão.

No entanto, MouseEvent (agora) indica quais botões estão pressionados no momento:

  • Para todos os navegadores modernos (exceto o Safari), use MouseEvent.buttons
  • Para o Safari, use MouseEvent.which( buttonsserá indefinido para o Safari) Nota: whichusa números diferentes dos buttonscliques direito e médio.

Quando registrado document, mousemoveserá acionado imediatamente assim que o cursor entrar novamente no navegador; portanto, se o usuário liberar fora, o estado será atualizado assim que o mouse voltar para dentro.

Uma implementação simples pode se parecer com:

var leftMouseButtonOnlyDown = false;

function setLeftButtonState(e) {
  leftMouseButtonOnlyDown = e.buttons === undefined 
    ? e.which === 1 
    : e.buttons === 1;
}

document.body.onmousedown = setLeftButtonState;
document.body.onmousemove = setLeftButtonState;
document.body.onmouseup = setLeftButtonState;

Se forem necessários cenários mais complicados (botões diferentes / vários botões / teclas de controle), consulte a documentação do MouseEvent. Quando / se o Safari melhorar o jogo, isso deve ficar mais fácil.

Jono Job
fonte
6
Este código verifica se APENAS o botão principal está pressionado e exige que todos os outros botões sejam pressionados. (por exemplo, se o primário e o secundário forem pressionados, e.buttonsserá 1+2=3, e.buttons === 1será falso). Se você quiser verificar se o botão principal é baixo, mas não se preocupam com quaisquer outros estados de botão, faça o seguinte:(e.buttons & 1) === 1
AJ Richardson
Tenho uma situação em que espero que, durante uma mudança de mouse, o código dos botões seja '1', mas produz '7' - todo o trabalho é no Chrome. Alguém tem alguma ideia?
Vasily Hall
@VasilyHall Olhando para os documentos MDN MouseEvent.buttons, parece que isso 7significa que ele acha que os botões Primário, Secundário e Auxiliar estão todos pressionados juntos. Não sei por que esse seria o caso - você está usando um mouse avançado? Se assim for, talvez valha a pena tentar com um básico. Se você está apenas começando, pode usar uma verificação bit a bit para garantir que o botão esquerdo seja pressionado e ignorar outros. por exemplo. MouseEvent.buttons & 1 === 1.
quer
Obrigado, eu estava usando meu JavaScript dentro de um navegador especial: o Chrome (ium?) Incorporado no aplicativo Adobe Illustrator - aposto que a combinação de todas as várias coisas leva a um 7, embora apenas um botão esteja sendo pressionado . Considerando que isso é consistente no meu aplicativo, estou simplesmente verificando 1 ou 7 para facilitar minha interatividade. Acrescentarei, embora eu use o mouse trackball Logitech M570 (ou o que seja), ele registra corretamente o 1 no navegador normal, mas o 7 dentro do Illustrator.
Vasily Hall
16

Eu acho que a melhor abordagem para isso é manter seu próprio registro do estado do botão do mouse, da seguinte maneira:

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

e depois, mais tarde no seu código:

if (mouseDown == 1) {
    // the mouse is down, do what you have to do.
}
paxdiablo
fonte
+1 e obrigado. Eu pensei que talvez tivesse que recorrer a isso, mas esperava que houvesse algum recurso que permitisse que você simplesmente checa diretamente o estado.
TM.
12
Existe uma razão para não usar um booleano?
Moala
1
@moala Acho Int parece ser menos digitação, e desempenho em última análise, um pouco melhor: jsperf.com/bool-vs-int
Johnathan Elmore
12

a solução não é boa. pode-se "reduzir o mouse" no documento e, em seguida, "passar o mouse" fora do navegador e, nesse caso, o navegador ainda estaria pensando que o mouse está desativado.

a única boa solução é usar o objeto IE.event.

Alfredo
fonte
11
Vejo de onde você é, mas ter uma solução que só funciona para o IE também não é uma boa solução.
TM.
@alfred Em alguns casos, pode ser útil saber se o mousemove está fora da janela. If (event.target.nodeName.toLowerCase === 'html') down = 0;
BF
6

Sei que este é um post antigo, mas achei que o rastreamento do botão do mouse usando o mouse para cima / baixo era um pouco desajeitado, então encontrei uma alternativa que pode agradar a alguns.

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

O seletor: active lida com o clique do mouse muito melhor do que o mouse para cima / baixo, você só precisa de uma maneira de ler esse estado no evento onmousemove. Para isso, eu precisava trapacear e confiei no fato de que o cursor padrão é "auto" e apenas mudei para "default", que é o que o auto seleciona por padrão.

Você pode usar qualquer coisa no objeto retornado pelo getComputedStyle que possa ser usado como um sinalizador sem alterar a aparência da sua página, por exemplo, cor da borda.

Eu gostaria de definir meu próprio estilo definido pelo usuário na seção: active, mas não consegui fazer isso funcionar. Seria melhor se fosse possível.

Martin
fonte
Obrigado por compartilhar isso. Eu estava usando essa idéia para uma lógica estilo drag-n-drop, mas enfrentou um problema no IE e teve que incluem lógica adicional
Eyup Yusein
4

O seguinte snippet tentará executar a função "doStuff" 2 segundos após o evento mouseDown ocorrer em document.body. Se o usuário levantar o botão, o evento mouseUp ocorrerá e cancelará a execução atrasada.

Eu aconselho o uso de algum método para anexação de eventos entre navegadores - a configuração explícita das propriedades de mouse e mouse foi feita para simplificar o exemplo.

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

fonte
Obrigado, mas esse não é o comportamento que eu estava procurando (embora eu possa ver como minha pergunta indica que é isso que estou tentando fazer).
TM.
3

Curto e grosso

Não sei por que nenhuma das respostas anteriores funcionou para mim, mas descobri essa solução durante um momento eureka. Não só funciona, mas também é mais elegante:

Adicionar à etiqueta do corpo:

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

Em seguida, teste e execute myfunction()se for downigual a 1:

onmousemove="if (down==1) myfunction();"
Stanley
fonte
4
você é novo, então eu vou ajudá-lo um pouco, aqui. Primeiro, lembre-se de sua gramática em suas postagens - provavelmente passei mais tempo corrigindo-a do que você coloca nela. Segundo, sua solução é apenas outra versão das outras listadas acima - não é novidade. Terceiro, sua solução usa uma técnica chamada "usando sinalizadores", sugerida por Thomas Hansen acima. Verifique sua gramática e não repita as soluções ao postar em postagens mais antigas sem fornecer alguma explicação sobre o motivo pelo qual as outras soluções não funcionaram para você.
Zachary Kniebel
5
No entanto, votei na sua solução, pois ela é válida e você é novo no SO
Zachary Kniebel 2/12/12
2

Você pode combinar o @Pax e minhas respostas para também obter a duração em que o mouse ficou inativo:

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

Depois, mais tarde:

if (mousedown >= 2000) {
  // do something if the mousebutton has been down for at least 2 seconds
}

fonte
Não é uma má ideia, Jake. Você precisa limparInterval () em onmouseup () antes de definir o mouse para 0? Desconfio que a função do timer ative entre configurá-lo para 0 e parar o timer, deixando o tempo limite em 200? Da mesma forma em onmousedown.
paxdiablo
A ordem dos clearIntervals não fará diferença, pois o encadeamento atual de execução será concluído antes que qualquer evento (incluindo timers) seja acionado.
Nathaniel Reinhart
2

Você precisa lidar com o MouseDown e o MouseUp e definir alguma bandeira ou algo para rastreá-lo "mais tarde na estrada" ... :(

Thomas Hansen
fonte
2

Se você estiver trabalhando em uma página complexa com manipuladores de eventos de mouse existentes, recomendo manipular o evento na captura (em vez de bolha). Para fazer isso, basta definir o terceiro parâmetro de addEventListenercomo true.

Além disso, convém verificar event.whichse você está lidando com a interação real do usuário e não com os eventos do mouse, por exemplo 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);

Adicione o manipulador ao documento (ou janela) em vez de document.body é importante porque ele garante que os eventos de mouse fora da janela ainda sejam registrados.

Micah Miller-Eshleman
fonte
1

Usando a API MouseEvent , para verificar o botão pressionado, se houver:

document.addEventListener('mousedown', (e) => console.log(e.buttons))

Retorno :

Um número representando um ou mais botões. Para mais de um botão pressionado simultaneamente, os valores são combinados (por exemplo, 3 é primário + secundário).

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)
NVRM
fonte
0

Usando o jQuery, a solução a seguir lida com o "arraste da página e solte o caso".

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

Não sei como ele lida com vários botões do mouse. Se houvesse uma maneira de iniciar o clique fora da janela, traga o mouse para dentro da janela e provavelmente isso também não funcionaria corretamente.

David
fonte
0

Abaixo do exemplo do jQuery, quando o mouse está sobre $ ('. Element'), a cor muda de acordo com o botão do mouse pressionado.

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();
});
Marcin Żurek
fonte
0

Como disse o @Jack, quando mouseupacontece fora da janela do navegador, não temos conhecimento disso ...

Este código (quase) funcionou para mim:

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

Infelizmente, não receberei o mouseupevento em um desses casos:

  • o usuário pressiona simultaneamente uma tecla do teclado e um botão do mouse, libera o botão do mouse fora da janela do navegador e libera a tecla.
  • usuário pressiona dois botões do mouse simultaneamente, libera um botão do mouse e o outro, ambos fora da janela do navegador.
dreadcast
fonte
-1

Bem, você não pode verificar se está inativo após o evento, mas pode verificar se está ativo ... se ativo .. significa que não está mais ativo: P lol

Então o usuário pressiona o botão (evento onMouseDown) ... e depois disso, você verifica se está ativo (onMouseUp). Enquanto não estiver funcionando, você pode fazer o que precisa.

rogeriopvl
fonte
2
O onMouseUp também não é um evento? Como você verificará a "propriedade" noMouseup? Ou de quem é a propriedade?
22420 Rohit
@ Ohh: você não verifica a propriedade, gerencia uma bandeira indicando que o evento aconteceu ... O botão do mouse está para cima ou para baixo.
PhiLho
-1
        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;
        }

esta versão entre navegadores funciona bem para mim.

user2550945
fonte