JavaScript: Sprawdź, czy przycisk myszy jest wciśnięty?

136

Czy istnieje sposób na wykrycie, czy przycisk myszy w JavaScript jest obecnie wyłączony?

Wiem o zdarzeniu „mousedown”, ale nie tego potrzebuję. Jakiś czas PO naciśnięciu przycisku myszy chcę móc wykryć, czy nadal jest wciśnięty.

czy to możliwe?

TM.
źródło
14
Dlaczego akceptujesz odpowiedź, która nie odpowiada na twoje pytanie. Rejestrowanie przesunięcia myszy i przesunięcia myszy jest nieprawidłowe (ponieważ możesz przesunąć kursor myszy poza przeglądarkę i pozwolić jej myśleć, że nadal przeciągasz). Przychodzenie po problemie i wchodzenie na tę stronę jest całkowicie bezużyteczne
Jack
@Jack to odpowiada na moje pytanie. Przeczytaj moje pytanie, a następnie przeczytaj odpowiedź. Najwyraźniej ponad 100 innych osób zgadza się, że to była dobra odpowiedź.
TM.
4
tak ... ale ja, Alfred i inne 8 mówimy ci, że śledzenie myszy i przesunięcia myszy nie jest właściwą drogą. Prawidłowa odpowiedź powinna obejmować analizę obiektu zdarzenia w wybranym programie obsługi lub analizę wszystkich zdarzeń związanych z myszą (takich jak wejście / wyjście), jeśli potrzebujesz sprawdzenia sterowanego czasem zamiast sprawdzenia sterowanego zdarzeniami.
Jack
To pytanie zostało zadane i udzielono odpowiedzi 7+ lat temu. Nie otrzymuję powiadomienia, gdy są nowe odpowiedzi (ale robię to w przypadku komentarzy). Napisz lepszą odpowiedź, a społeczność zagłosuje za nią na szczycie :) Wiele informacji jest nieaktualnych i wiele się zmienia.
TM.
2
@TM. Przepraszam za necro, ale ... Nie, te informacje nigdy nie dotrą na szczyt. Ktoś, kto nie odpowiada na własną rękę, raz zaakceptowany, pozostaje na szczycie na zawsze. Ponadto „och, tak wielu ludzi głosowało na ten” nie oznacza, że ​​odpowiedź jest dobra; oznacza to po prostu, że wiele osób to widziało, uważało, że wygląda dobrze, i zdobyło uznanie, ponieważ wydawało się, że działa dla nich.
Załóż pozew Moniki

Odpowiedzi:

147

Odnośnie rozwiązania Pax: nie działa, jeśli użytkownik celowo lub przypadkowo kliknie więcej niż jeden przycisk. Nie pytaj mnie, skąd wiem :-(.

Prawidłowy kod powinien wyglądać tak:

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

Z takim testem:

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

Jeśli chcesz wiedzieć, który przycisk jest wciśnięty, przygotuj się na zrobienie mouseDown tablicę liczników i policz je osobno dla oddzielnych przycisków:

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

Teraz możesz sprawdzić, jakie dokładnie przyciski zostały wciśnięte:

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

Teraz ostrzegamy, że powyższy kod będzie działał tylko w przeglądarkach zgodnych ze standardami, które przekazują ci numer przycisku zaczynający się od 0 i wyżej. IE używa maski bitowej aktualnie wciśniętych przycisków:

  • 0 dla „nic nie jest wciśnięte”
  • 1 dla lewej
  • 2 na prawo
  • 4 na środku
  • i dowolna kombinacja powyższych, np. 5 dla lewej + środka

Więc odpowiednio dostosuj swój kod! Zostawiam to jako ćwiczenie.

I pamiętaj: IE używa globalnego obiektu zdarzenia o nazwie… „zdarzenie”.

Nawiasem mówiąc, IE ma funkcję przydatną w twoim przypadku: kiedy inne przeglądarki wysyłają „przycisk” tylko dla zdarzeń związanych z przyciskiem myszy (onclick, onmousedown i onmouseup), IE wysyła go również z onmousemove. Możesz więc zacząć nasłuchiwać onmousemove, gdy potrzebujesz znać stan przycisku, i sprawdzać, czy występuje evt.button, gdy tylko go otrzymasz - teraz wiesz, jakie przyciski myszy zostały naciśnięte:

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

Oczywiście nic nie dostaniesz, jeśli udaje martwą i się nie rusza.

Odpowiednie linki:

Aktualizacja nr 1 : Nie wiem, dlaczego przeniosłem kod w stylu document.body. Lepiej będzie dołączyć programy obsługi zdarzeń bezpośrednio do dokumentu.

Eugene Lazutkin
źródło
1
Po przetestowaniu okazuje się, że przycisk evt.button w rzeczywistości nie istnieje onmousemove z IE7 ...
TM.
5
Myślę, że to nie zadziała w Chrome, ponieważ Chrome nie generuje zdarzeń myszy, jeśli oryginalne przesunięcie myszy nastąpiło na pasku przewijania. Przykład z tego wydania Chrome / Chromium . Więc twój lewy przycisk myszy byłby na zawsze wciśnięty, jeśli istnieje pasek przewijania, którego ktoś używa! (W przeglądarce Chrome)
KajMagnus
1
Uwielbiam twoje rozwiązanie, ale co, jeśli mysz pochodzi z innej aplikacji, powiedz słowo ms i przeciągnie jakiś tekst. Jak wykryjesz, że mysz nie działa?
Shadrack B. Orina,
6
Nie rozumiem, dlaczego w drugim przykładzie zwiększasz wartość elts tablicy myszy. Dlaczego licznik dla każdego przycisku zamiast wartości bool dla każdego przycisku?
zimą
2
To bardzo popularne pytanie (i odpowiedź). Dziwię się, że to jeszcze nie wyszło, ale nie musisz w ogóle przechowywać danych myszy. Możesz po prostu podłączyć event.buttonsdowolny wyzwalacz zdarzenia, którego używasz, bez zewnętrznych zapisanych zmiennych. Zadałem zupełnie nowe pytanie (ponieważ moje zdarzenie mouseup nie zawsze się uruchamia, więc to rozwiązanie nie działa dla mnie) i otrzymałem tę odpowiedź w ciągu może 5 minut.
RoboticRenaissance
32

To jest stare pytanie, a odpowiedzi tutaj wydają się być zwolennikami używania mousedowni mouseupśledzenia, czy przycisk jest wciśnięty. Ale jak zauważyli inni,mouseup będzie uruchamiany tylko wtedy, gdy jest wykonywany w przeglądarce, co może prowadzić do utraty śledzenia stanu przycisku.

Jednak MouseEvent (teraz) wskazuje, które przyciski są aktualnie wciśnięte:

  • W przypadku wszystkich nowoczesnych przeglądarek (z wyjątkiem Safari) użyj MouseEvent.buttons
  • W przypadku Safari użyj MouseEvent.which( buttonsbędzie niezdefiniowane dla Safari) Uwaga: whichużywa innych liczb niż buttonsdla kliknięć prawym i środkowym.

Gdy zarejestrowana w dniu document,mousemove natychmiast zwolnić, gdy tylko kursor ponownie przechodzi przeglądarkę, więc jeśli użytkownik zwalnia poza to państwo będzie aktualizowany jak tylko mysz z powrotem do środka.

Prosta implementacja może wyglądać następująco:

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;

Jeśli wymagane są bardziej skomplikowane scenariusze (różne przyciski / wiele przycisków / klawisze kontrolne), zapoznaj się z dokumentacją MouseEvent. Kiedy / jeśli Safari podniesie swoją grę, powinno to być łatwiejsze.

Jono Job
źródło
6
Ten kod sprawdza, czy TYLKO główny przycisk jest wciśnięty i wymaga zwolnienia wszystkich innych przycisków. (np. jeśli naciśnięte e.buttonszostaną zarówno podstawowe, jak i dodatkowe, będzie 1+2=3, więc e.buttons === 1będzie fałszywe). Jeśli chcesz sprawdzić, czy podstawowy przycisk jest w dół, ale nie dbają o innych stanów przycisku, to zrobić:(e.buttons & 1) === 1
AJ Richardson
Mam sytuację, w której spodziewam się, że podczas poruszania myszą powinienem uzyskać kod przycisków na „1”, ale daje „7”, - cała praca jest w Chrome. Ktoś ma jakiś pomysł?
Wasilij Hall
@VasilyHall Patrząc na dokumenty MDN dla MouseEvent.buttons, wydaje się, że 7oznacza to, że myśli, że przyciski Primary, Secondary i Auxilary są wciśnięte razem. Nie jestem pewien, dlaczego tak się dzieje - czy używasz zaawansowanej myszy? Jeśli tak, to może warto spróbować z podstawowym. Jeśli potrzebujesz tylko uruchomić, możesz użyć bitowego sprawdzenia, aby upewnić się, że lewy przycisk jest naciśnięty i zignorować inne. na przykład. MouseEvent.buttons & 1 === 1.
Jono Job
Dziękuję, używałem JavaScript w specjalnej przeglądarce: wbudowanej przeglądarce Chrome (ium?) W aplikacji Adobe Illustrator - założę się, że połączenie różnych rzeczy w jakiś sposób prowadzi do 7, chociaż naciskany jest tylko jeden przycisk . Widząc, jak to jest spójne w mojej aplikacji, po prostu sprawdzam 1 lub 7, aby ułatwić moją interaktywność. Dodam, że chociaż używam myszy Logitech M570 (lub cokolwiek), to poprawnie rejestruje 1 w zwykłej przeglądarce, ale 7 w programie Illustrator.
Vasily Hall
16

Myślę, że najlepszym podejściem jest zachowanie własnego zapisu stanu przycisku myszy w następujący sposób:

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

a później w kodzie:

if (mouseDown == 1) {
    // the mouse is down, do what you have to do.
}
paxdiablo
źródło
+1 i dzięki. Pomyślałem, że może będę musiał się do tego uciec, ale miałem nadzieję, że jest jakaś funkcja, która pozwala po prostu bezpośrednio sprawdzić stan.
TM.
12
Czy istnieje powód, aby nie używać wartości logicznej?
moala
1
@moala Myślę, że Int wydaje się mniej pisać, a ostatecznie nieco lepszą wydajność: jsperf.com/bool-vs-int
Johnathan Elmore
12

rozwiązanie nie jest dobre. można by przesunąć kursor myszy na dokument, a następnie przesunąć mysz poza przeglądarkę, aw tym przypadku przeglądarka nadal myślałaby, że mysz nie działa.

jedynym dobrym rozwiązaniem jest użycie obiektu IE.event.

Alfred
źródło
11
Widzę, skąd pochodzisz, ale posiadanie rozwiązania, które działa tylko w IE, również nie jest dobrym rozwiązaniem.
TM.
@alfred W niektórych przypadkach warto wiedzieć, czy Mousemove jest poza oknem. Jeśli (event.target.nodeName.toLowerCase === 'html') down = 0;
BF
6

Wiem, że to stary post, ale pomyślałem, że śledzenie przycisku myszy za pomocą myszy w górę / w dół wydawało się nieco niezgrabne, więc znalazłem alternatywę, która może spodobać się niektórym.

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

Selektor: active radzi sobie z kliknięciem myszy znacznie lepiej niż myszką w górę / w dół, potrzebujesz tylko sposobu na odczytanie tego stanu w zdarzeniu onmousemove. W tym celu musiałem oszukiwać i polegać na fakcie, że domyślny kursor to „auto” i po prostu zmieniam go na „domyślny”, czyli to, co domyślnie wybiera automatycznie.

Możesz użyć w obiekcie wszystkiego, co jest zwracane przez getComputedStyle, czego możesz użyć jako flagi bez zmiany wyglądu strony, np. Border-color.

Chciałbym ustawić własny styl zdefiniowany przez użytkownika w sekcji: active, ale nie mogłem tego zmusić. Byłoby lepiej, gdyby to było możliwe.

Jaskółka oknówka
źródło
Dzięki za udostępnienie tego.
Używałem
4

Poniższy fragment kodu spróbuje wykonać funkcję „doStuff” 2 sekundy po wystąpieniu zdarzenia mouseDown w pliku document.body. Jeśli użytkownik podniesie przycisk, nastąpi zdarzenie mouseUp i anuluje opóźnione wykonanie.

Radziłbym użyć jakiejś metody dołączania zdarzeń w różnych przeglądarkach - jawne ustawienie właściwości myszy i myszy zostało zrobione w celu uproszczenia przykładu.

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

źródło
Dzięki, ale to nie jest do końca zachowanie, którego szukałem (chociaż widzę, jak moje pytanie wskazuje, że właśnie to próbuję zrobić).
TM.
3

Krótkie i słodkie

Nie jestem pewien, dlaczego żadna z poprzednich odpowiedzi nie zadziałała, ale wpadłem na to rozwiązanie w chwili eureki. Nie tylko działa, ale jest też najbardziej elegancki:

Dodaj do tagu ciała:

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

Następnie przetestuj i wykonaj, myfunction()jeśli downrówna się 1:

onmousemove="if (down==1) myfunction();"
Stanley
źródło
4
jesteś nowy, więc pomogę ci trochę tutaj. Po pierwsze, pamiętaj o gramatyce w swoich postach - prawdopodobnie spędziłem więcej czasu na poprawianiu tego, niż w to wkładałeś. Po drugie, Twoje rozwiązanie jest po prostu kolejną wersją innych wymienionych powyżej - to nic nowego. Po trzecie, Twoje rozwiązanie wykorzystuje technikę określaną jako „używanie flag”, którą zasugerował Thomas Hansen powyżej. Zwróć uwagę, aby sprawdzić swoją gramatykę i nie powtarzać rozwiązań podczas wysyłania do starszych postów bez podania wyjaśnienia, dlaczego inne rozwiązania nie zadziałały.
Zachary Kniebel
5
Głosowałem jednak za twoim rozwiązaniem, ponieważ JEST ono ważne i jesteś nowy w SO
Zachary Kniebel
2

Możesz połączyć @Pax i moje odpowiedzi, aby uzyskać również czas, przez jaki mysz była wyłączona:

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

Później:

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

źródło
To niezły pomysł, Jake. Czy musisz wyczyścić interval () w onmouseup () przed ustawieniem mousedown na 0? Obawiam się, że funkcja timera uruchamia się między ustawieniem jej na 0 a zatrzymaniem timera, pozostawiając limit czasu na 200? Podobnie w onmousedown.
paxdiablo
Kolejność clearIntervals nie będzie miała znaczenia, ponieważ bieżący wątek wykonania zakończy się przed uruchomieniem jakichkolwiek zdarzeń (w tym timerów).
Nathaniel Reinhart
2

Musisz obsłużyć MouseDown i MouseUp i ustawić jakąś flagę lub coś, aby śledzić to "później w drodze" ... :(

Thomas Hansen
źródło
2

Jeśli pracujesz na złożonej stronie z istniejącymi programami obsługi zdarzeń myszy, polecam obsługę zdarzenia podczas przechwytywania (zamiast bąbelków). Aby to zrobić, wystarczy ustawić trzeci parametr addEventListenerna true.

Ponadto możesz chcieć sprawdzić, event.whichczy obsługujesz rzeczywistą interakcję użytkownika, a nie zdarzenia myszy, np 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);

Dodaj moduł obsługi do dokumentu (lub okna) zamiast do document. Body jest ważne b / c zapewnia, że ​​zdarzenia myszy poza oknem są nadal rejestrowane.

Micah Miller-Eshleman
źródło
1

Używając interfejsu API MouseEvent , aby sprawdzić wciśnięty przycisk, jeśli jest dostępny:

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

Zwrot :

Liczba reprezentująca jeden lub więcej przycisków. W przypadku jednoczesnego naciśnięcia więcej niż jednego przycisku wartości są łączone (np. 3 oznacza główny + dodatkowy).

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
źródło
0

Korzystając z jQuery, poniższe rozwiązanie obsługuje nawet „przeciągnij stronę, a następnie zwolnij”.

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

Nie wiem, jak obsługuje wiele przycisków myszy. Gdyby istniał sposób na rozpoczęcie klikania poza oknem, a następnie przeniesienie myszy do okna, prawdopodobnie również tam nie działałoby to poprawnie.

David
źródło
0

Poniżej przykładu jQuery, kiedy mysz znajduje się nad $ ('. Element'), kolor zmienia się w zależności od tego, który przycisk myszy jest wciśnięty.

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
źródło
0

Jak powiedział @Jack, kiedy mouseup dzieje się poza oknem przeglądarki, nie jesteśmy tego świadomi ...

Ten kod (prawie) działał dla mnie:

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

Niestety nie dostanę mouseupzdarzenia w jednym z tych przypadków:

  • użytkownik jednocześnie naciska klawisz klawiatury i przycisk myszy, zwalnia przycisk myszy poza oknem przeglądarki, a następnie zwalnia klawisz.
  • użytkownik naciska jednocześnie dwa przyciski myszy, zwalnia jeden, a potem drugi, oba poza oknem przeglądarki.
dreadcast
źródło
-1

Cóż, nie możesz sprawdzić, czy jest wyłączony po wydarzeniu, ale możesz sprawdzić, czy jest włączony… Jeśli jest włączony… oznacza to, że już nie działa: P lol

Tak więc użytkownik naciska przycisk w dół (zdarzenie onMouseDown) ... a następnie sprawdzasz, czy jest włączony (onMouseUp). Chociaż nie jest to możliwe, możesz zrobić to, czego potrzebujesz.

rogeriopvl
źródło
2
Czy onMouseUp też nie jest wydarzeniem? Jak sprawdzisz „właściwość” naMouseup? A raczej czyja własność?
Rohit
@Rohit: nie sprawdzasz właściwości, zarządzasz flagą wskazującą, że zdarzenie miało miejsce ... Przycisk myszy jest albo w górę, albo w dół.
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;
        }

ta wersja dla różnych przeglądarek działa dobrze dla mnie.

user2550945
źródło