Wykrywanie, że przeglądarka nie ma myszy i obsługuje tylko dotyk


149

Tworzę aplikację internetową (nie witrynę internetową ze stronami z interesującym tekstem) z bardzo innym interfejsem dotykowym (palec chowa ekran po kliknięciu) i myszy (w dużej mierze opiera się na podglądzie po najechaniu kursorem). Jak mogę wykryć, że mój użytkownik nie ma myszy, aby pokazać mu właściwy interfejs? Planuję zostawić przełącznik dla osób z myszą i dotykiem (jak niektóre notebooki).

Możliwość obsługi zdarzenia dotykowego w przeglądarce nie oznacza w rzeczywistości, że użytkownik korzysta z urządzenia dotykowego (na przykład Modernizr go nie przerywa). Kod, który poprawnie odpowiada na pytanie, powinien zwracać fałsz, jeśli urządzenie jest wyposażone w mysz, w przeciwnym razie prawda. W przypadku urządzeń z myszą i dotykiem powinna zwracać wartość false (nie tylko dotyk)

Na marginesie, mój interfejs dotykowy może być również odpowiedni dla urządzeń obsługujących tylko klawiaturę, więc raczej chodzi o brak myszy, który chcę wykryć.

Aby wyjaśnić tę potrzebę, oto API, które chcę zaimplementować:

// Level 1


// The current answers provide a way to do that.
hasTouch();

// Returns true if a mouse is expected.
// Note: as explained by the OP, this is not !hasTouch()
// I don't think we have this in the answers already, that why I offer a bounty
hasMouse();

// Level 2 (I don't think it's possible, but maybe I'm wrong, so why not asking)

// callback is called when the result of "hasTouch()" changes.
listenHasTouchChanges(callback);

// callback is called when the result of "hasMouse()" changes.
listenHasMouseChanges(callback);


Myślę, że musisz przemyśleć swój projekt, jeśli chcesz, aby jedna aplikacja miała zastosowanie zarówno na komputerze stacjonarnym, jak i na urządzeniu mobilnym / dotykowym, ale miała różne zachowania dla każdej z nich. Nie sądzę, aby to, czego szukasz, było w tej chwili możliwe, ponieważ szybkie wyszukiwanie w Google hasła „mysz wykrywająca javascript” pokazuje jeden umiarkowanie przydatny post na quirksmode.org do wykrywania różnych stanów myszy (kliknięcia, pozycja, itp.), ale ZERO zależy od tego, czy mysz rzeczywiście istnieje.
davethegr8

24
Może dlatego, że Google nie pomogło, że poprosiłem o to tutaj.
nraynaud

2
Czy próbowałeś / aś Document Mouseenter z jQuery? $ (dokument) .mouseenter (funkcja (e) {alert ("mysz");});
Parag Gajjar

4
Po rozważeniu prawie tuzina obiecujących możliwości tylko po to, aby w ciągu kilku minut odrzucić każdą z nich, to pytanie doprowadza mnie do szału.
Jordan Grey

Odpowiedzi:


65

Główny problem polega na tym, że masz następujące różne klasy urządzeń / przypadków użycia:

  1. Mysz i klawiatura (komputer stacjonarny)
  2. Tylko dotyk (telefon / tablet)
  3. Mysz, klawiatura i dotyk (laptopy dotykowe)
  4. Dotyk i klawiatura (klawiatura bluetooth na tablecie)
  5. Tylko mysz (wyłączone preferencje użytkownika / przeglądania)
  6. Tylko klawiatura (wyłączone preferencje użytkownika / przeglądania)
  7. Dotyk i mysz (tj. Zdarzenia najechania kursorem z pióra Galaxy Note 2)

Co gorsza, można przejść z niektórych z tych klas do innych (podłącza się mysz, łączy się z klawiaturą), lub użytkownik może WYGLĄDAĆ na zwykłego laptopa, dopóki nie wyciągnie ręki i nie dotknie ekranu.

Masz rację, zakładając, że obecność konstruktorów zdarzeń w przeglądarce nie jest dobrym sposobem na robienie postępów (i jest nieco niespójna). Ponadto, jeśli nie śledzisz bardzo konkretnego zdarzenia lub tylko próbujesz wykluczyć kilka powyższych klas, użycie samych zdarzeń nie jest pełnym dowodem.

Załóżmy na przykład, że odkryłeś, że użytkownik wyemitował prawdziwe ruchy myszy (a nie fałszywe ze zdarzeń dotykowych, zobacz http://www.html5rocks.com/en/mobile/touchandmouse/ ).

Więc co?

Włączasz style najechania? Dodajesz więcej przycisków?

Tak czy inaczej, wydłużasz czas potrzebny na szkło, ponieważ musisz poczekać na uruchomienie zdarzenia.

Ale co się stanie, gdy twój szlachetny użytkownik zdecyduje się odłączyć mysz i przejść do pełnego kontaktu ... czy czekasz, aż dotknie twojego teraz zapchanego interfejsu, a następnie zmień go zaraz po tym, jak spróbuje zlokalizować twój teraz zatłoczony interfejs użytkownika?

W formie punktorów, cytując stucox na https://github.com/Modernizr/Modernizr/issues/869#issuecomment-15264101

  • Chcemy wykryć obecność myszy
  • Ae prawdopodobnie nie może wykryć przed uruchomieniem zdarzenia
  • W związku z tym wykrywamy to, czy w tej sesji została użyta mysz - nie nastąpi to natychmiast po załadowaniu strony
  • Prawdopodobnie nie możemy również wykryć, że nie ma myszy - byłoby to niezdefiniowane, dopóki nie jest prawdziwe (myślę, że ma to więcej sensu niż ustawianie fałszywej, dopóki nie zostanie udowodnione)
  • I prawdopodobnie nie możemy wykryć, czy mysz jest odłączona w trakcie sesji - będzie to nie do odróżnienia od użytkownika po prostu rezygnującego z myszy

Na marginesie: przeglądarka WIE, kiedy użytkownik podłącza mysz / klawiaturę, ale nie wystawia jej na działanie JavaScript ... dang!

Powinno to doprowadzić do następujących sytuacji:

Śledzenie aktualnych możliwości danego użytkownika jest skomplikowane, zawodne i wątpliwe

Jednak idea stopniowego ulepszania ma tutaj zastosowanie. Zbuduj środowisko, które działa płynnie bez względu na kontekst użytkownika. Następnie przyjmij założenia na podstawie funkcji przeglądarki / zapytań o media, aby dodać funkcjonalność, która będzie względna w założonym kontekście. Obecność myszy to tylko jeden z wielu sposobów, w jakie różni użytkownicy różnych urządzeń korzystają z Twojej witryny. Stwórz coś wartościowego w swoim jądrze i nie przejmuj się zbytnio tym, jak ludzie klikają przyciski.


3
świetna odpowiedź. Miejmy nadzieję, że użytkownik zawsze ma ekran! Myślę, że warto zbudować interfejs, który będzie dostosowany do aktualnego trybu interakcji użytkownika. Na laptopie dotykowym sensowne jest dostosowanie aplikacji (tj. :hoverElementów i tym podobnych), gdy użytkownik przełącza się z myszy na dotyk. Wydaje się mało prawdopodobne, że użytkownik aktualnie używa myszy + dotyku w tym samym czasie (mam na myśli comoonn to tak, jakby mieć 2 myszy podłączone do tego samego komputera hahaha)
Sebastien Lorber

@SebastienLorber - nienawidzę złamać go do ciebie, ale użytkownicy nie muszą zawsze mieć ekran. ( Czy można użyć javascript do wykrycia, czy czytnik ekranu działa na komputerze użytkownika? )
ashleedawg

57

Co powiesz na nasłuchiwanie zdarzenia mousemove w dokumencie. Następnie, dopóki nie usłyszysz tego zdarzenia, zakładasz, że urządzenie jest tylko dotykowe lub klawiaturowe.

var mouseDetected = false;
function onMouseMove(e) {
  unlisten('mousemove', onMouseMove, false);
  mouseDetected = true;
  // initializeMouseBehavior();
}
listen('mousemove', onMouseMove, false);

(Gdzie listeni unlistendeleguj addEventListenerlub attachEventodpowiednio.)

Miejmy nadzieję, że nie doprowadziłoby to do zbytniego wizualnego szarpnięcia, byłoby do niczego, gdybyś potrzebował ogromnych zmian układów opartych na trybie ...


2
To dobry pomysł, ale niestety opóźnienie w odpowiedzi sprawi, że będzie bezużyteczny, gdy interfejs użytkownika aplikacji zależy od tego, czy mysz jest dostępna. Jest to szczególnie ważne, jeśli aplikacja może być umieszczona w ramce iframe, więc zdarzenia myszy będą uderzać tylko wtedy, gdy mysz przesuwa się nad samą
ramką

7
Może to zadziałać, jeśli aplikacja zostanie uruchomiona z ekranem powitalnym i przyciskiem „kontynuuj”. Jeśli mysz poruszy się przed pierwszym zdarzeniem mousedown, masz mysz. Nie powiedzie się tylko wtedy, gdy przycisk zostanie załadowany bezpośrednio pod myszką, a użytkownik ma bardzo stabilną rękę (należy podnieść nawet poruszający się 1 piksel).
SpliFF

49
fajny pomysł, ale nie wydaje się działać w naszych testach . iPady wyzwalają to zdarzenie.
Jeff Atwood,

3
@JeffAtwood, co ostatecznie zrobiłeś w swoim przypadku?
Michael Haren

2
iPad zdecydowanie wyzwala zdarzenie mousemove, tuż przed zdarzeniem mousedown. Zauważyłem, że liczba naciśnięć myszy> 0 i liczba naciśnięć myszy == liczba ruchów myszy to dobry sposób na wykrycie braku myszy. Nie mogę tego powtórzyć za pomocą prawdziwej myszy.
Peter Wooster

36

Od 2018 roku istnieje dobry i niezawodny sposób wykrywania, czy przeglądarka ma mysz (lub podobne urządzenie wejściowe): funkcje interakcji z mediami CSS4, które są teraz obsługiwane przez prawie każdą nowoczesną przeglądarkę (z wyjątkiem IE 11 i specjalnych przeglądarek mobilnych).

W3C:

Funkcja wskaźnika multimedialnego służy do sprawdzania obecności i dokładności urządzenia wskazującego, takiego jak mysz.

Zobacz następujące opcje:

    /* The primary input mechanism of the device includes a 
pointing device of limited accuracy. */
    @media (pointer: coarse) { ... }

    /* The primary input mechanism of the device 
includes an accurate pointing device. */
    @media (pointer: fine) { ... }

    /* The primary input mechanism of the 
device does not include a pointing device. */
    @media (pointer: none) { ... }

    /* Primary input mechanism system can 
       hover over elements with ease */
    @media (hover: hover) { ... }

    /* Primary input mechanism cannot hover 
       at all or cannot conveniently hover 
       (e.g., many mobile devices emulate hovering
       when the user performs an inconvenient long tap), 
       or there is no primary pointing input mechanism */
    @media (hover: none) { ... }

    /* One or more available input mechanism(s) 
       can hover over elements with ease */
    @media (any-hover: hover) { ... }


    /* One or more available input mechanism(s) cannot 
       hover (or there are no pointing input mechanisms) */
    @media (any-hover: none) { ... }

Zapytania o media mogą być również używane w JS:

if(window.matchMedia("(any-hover: none)").matches) {
    // do sth
}

Związane z:

Dokumentacja W3: https://www.w3.org/TR/mediaqueries-4/#mf-interaction

Obsługa przeglądarek: https://caniuse.com/#search=media%20features

Podobny problem: Wykryj, czy urządzenie klienckie obsługuje stany: hover i: focus


Osobiście podoba mi się ta odpowiedź, ale na razie (10/19) zapytania @media hover i pointer CSS są dostępne tylko na ~ 85% urządzeń na całym świecie według caniuse.com. Z pewnością nieźle, preferowane jest 95% lub więcej. Miejmy nadzieję, że wkrótce stanie się to standardem na urządzeniach.
MQuiggGeorgia

1
@MQuiggGeorgia Zasadniczo zgadzam się z twoją krytyką, nie wszędzie jest ona jeszcze obsługiwana. Nadal caniuse.com twierdzi, że jest obsługiwany w 91,2% ( caniuse.com/#feat=css-media-interaction ). Po bliższym przyjrzeniu się jest obsługiwany wszędzie z wyjątkiem IE 11 i specjalnych spłaszczonych przeglądarek na urządzeniach mobilnych. Aby być uczciwym, dotyczy to każdej nowoczesnej funkcji, ponieważ Microsoft już dawno przestał wdrażać funkcje IE. W przypadku przeglądarki IE 11 możesz użyć rozwiązania zastępczego z innych odpowiedzi tutaj.
Blackbam

23

Odpowiedź @ Wyatta jest świetna i daje nam wiele do przemyślenia.

W moim przypadku zdecydowałem się nasłuchiwać pierwszej interakcji, a dopiero potem ustawić zachowanie. Więc nawet jeśli użytkownik ma mysz, będę traktować jako urządzenie dotykowe, jeśli pierwsza interakcja była dotykiem.

Biorąc pod uwagę podaną kolejność przetwarzania zdarzeń :

  1. touchstart
  2. touchmove
  3. touchend
  4. najechanie myszą
  5. mousemove
  6. mousedown
  7. mouseup
  8. Kliknij

Możemy założyć, że jeśli zdarzenie myszy zostanie wyzwolone przed dotykiem, jest to prawdziwe zdarzenie myszy, a nie emulowane. Przykład (przy użyciu jQuery):

$(document).ready(function() {
    var $body = $('body');
    var detectMouse = function(e){
        if (e.type === 'mousedown') {
            alert('Mouse interaction!');
        }
        else if (e.type === 'touchstart') {
            alert('Touch interaction!');
        }
        // remove event bindings, so it only runs once
        $body.off('mousedown touchstart', detectMouse);
    }
    // attach both events to body
    $body.on('mousedown touchstart', detectMouse);
});

To zadziałało dla mnie


U mnie nie działa, Ipad Safari (IOS8.3) również wykrywa mysz z tym fragmentem
kodu

3
@netzaffin. Dzięki za informację zwrotną okazało się, że bardziej spójne jest użycie przycisku myszy zamiast najeżdżania myszą. Czy mógłbyś rzucić okiem na te skrzypce ze swojego IOS i poinformować mnie o wyniku? Pozdrawiam jsfiddle.net/bkwb0qen/15/embedded/result
Hugo Silva

1
Jeśli masz ekran dotykowy z myszą, wykryta zostanie tylko metoda wprowadzania użyta jako pierwsza.
0xcaff

11

Można tylko wykryć, czy przeglądarka obsługuje dotyk . Nie ma sposobu, aby dowiedzieć się, czy faktycznie ma podłączony ekran dotykowy lub mysz.

Można jednak ustalić priorytety użytkowania, nasłuchując zdarzenia dotyku zamiast zdarzenia myszy, jeśli wykryta zostanie funkcja dotyku.

Aby wykryć obsługę dotykową w różnych przeglądarkach:

function hasTouch() {
    return (('ontouchstart' in window) ||       // html5 browsers
            (navigator.maxTouchPoints > 0) ||   // future IE
            (navigator.msMaxTouchPoints > 0));  // current IE10
}

Następnie można to wykorzystać do sprawdzenia:

if (!hasTouch()) alert('Sorry, need touch!);

lub wybrać, którego zdarzenia słuchać, albo:

var eventName = hasTouch() ? 'touchend' : 'click';
someElement.addEventListener(eventName , handlerFunction, false);

lub użyj oddzielnych podejść dla dotyku i niedotyku:

if (hasTouch() === true) {
    someElement.addEventListener('touchend' , touchHandler, false);

} else {
    someElement.addEventListener('click' , mouseHandler, false);

}
function touchHandler(e) {
    /// stop event somehow
    e.stopPropagation();
    e.preventDefault();
    window.event.cancelBubble = true;
    // ...
    return false; // :-)
}
function mouseHandler(e) {
    // sorry, touch only - or - do something useful and non-restrictive for user
}

W przypadku myszy można tylko wykryć, czy mysz jest używana, a nie, czy istnieje, czy nie. Można ustawić globalną flagę, aby wskazać, że mysz została wykryta przez użycie (podobnie do istniejącej odpowiedzi, ale nieco uproszczonej):

var hasMouse = false;

window.onmousemove = function() {
    hasMouse = true;
}

(nie można uwzględniać mouseupani, mousedownponieważ zdarzenie to można również wywołać dotykiem)

Przeglądarki ograniczają dostęp do niskopoziomowych interfejsów API systemu, które są potrzebne do wykrywania funkcji, takich jak możliwości sprzętowe systemu, w którym są używane.

Istnieje możliwość napisania wtyczki / rozszerzenia, aby uzyskać do nich dostęp, ale za pomocą JavaScript i DOM takie wykrywanie jest ograniczone do tego celu i należałoby napisać wtyczkę specyficzną dla różnych platform OS.

Podsumowując: takie wykrycie można oszacować jedynie na podstawie „dobrego przypuszczenia”.


8

Kiedy Media Queries Level 4 będą dostępne w przeglądarkach, będziemy mogli używać zapytań „wskaźnik” i „hover” do wykrywania urządzeń za pomocą myszy.

Jeśli naprawdę chcemy przekazać te informacje do Javascript, możemy użyć zapytania CSS, aby ustawić określone style zgodnie z typem urządzenia, a następnie użyć getComputedStylew Javascript do odczytania tego stylu i wyprowadzenia z niego oryginalnego typu urządzenia.

Ale mysz można podłączyć lub odłączyć w dowolnym momencie, a użytkownik może chcieć przełączać się między dotykiem a myszą. Dlatego może być konieczne wykrycie tej zmiany i zaproponowanie zmiany interfejsu lub zrobienie tego automatycznie.


1
W szczególności any-pointeriany-hover pozwoli Ci zbadać wszystkie odpowiednie możliwości urządzenia. Miło jest zobaczyć, jak możemy rozwiązać ten problem w przyszłości! :)
Jordan Grey

2
window.matchMedia ("(any-pointer: coarse)"). match === true?
4esn0k

7

Ponieważ i tak planujesz zaoferować sposób przełączania się między interfejsami, czy byłoby możliwe po prostu poprosić użytkownika o kliknięcie łącza lub przycisku w celu „wprowadzenia” prawidłowej wersji aplikacji? Wtedy można było zapamiętać ich preferencje co do przyszłych wizyt. To nie jest high-tech, ale jest w 100% niezawodny :-)


2
Właściwie to całkiem dobra sugestia, ale opóźnia czas, zanim użytkownik dotrze do prawdziwego interfejsu. Będę też musiał zapewnić sposób przełączania po początkowym wyborze. W efekcie jest więcej pracy, niż gdyby można to było po prostu wykryć ...
Jon Gjengset,

1
Zapytanie użytkownika jest najwyraźniej najlepszym sposobem - jeśli nie zawsze niezawodnym - i zapewnia wygodne miejsce do umieszczania powiadomień o uaktualnieniach, a co nie. Myślę, że przesadnie myślisz o „problemie” ...
T4NK3R

4

@SamuelRossille Żadne przeglądarki, o których jestem świadomy, nie ujawniają niestety istnienia (lub braku) myszy.

Mając to na uwadze, musimy po prostu spróbować i zrobić wszystko, co w naszej mocy, z naszą istniejącą opcją ... wydarzeniami. Wiem, że to nie jest dokładnie to, czego szukasz ... zgodził się, że obecnie jest daleki od ideału.

Możemy zrobić wszystko, co w naszej mocy, aby dowiedzieć się, czy użytkownik w danym momencie używa myszy, czy dotyku. Oto szybki i brudny przykład użycia jQuery & Knockout:

//namespace
window.ns = {};

// for starters, we'll briefly assume if touch exists, they are using it - default behavior
ns.usingTouch = ko.observable(Modernizr.touch); //using Modernizr here for brevity.  Substitute any touch detection method you desire

// now, let's sort out the mouse
ns.usingMouse = ko.computed(function () {
    //touch
    if (ns.usingTouch()) {
        //first, kill the base mousemove event
        //I really wish browsers would stop trying to handle this within touch events in the first place
        window.document.body.addEventListener('mousemove', function (e) {
            e.preventDefault();
            e.stopImmediatePropagation();
        }, true);

        //remove mouse class from body
        $('body').removeClass("mouse");

        //switch off touch listener
        $(document).off(".ns-touch");

        // switch on a mouse listener
        $(document).on('mousemove.ns-mouse', function (e) {
            if (Math.abs(window.lastX - e.clientX) > 0 || window.lastY !== e.clientY) {
                ns.usingTouch(false);  //this will trigger re-evaluation of ns.usingMouse() and result in ns.usingMouse() === true
            }
        });

        return false;
    }
    //mouse
    else {
        //add mouse class to body for styling
        $('body').addClass("mouse");

        //switch off mouse listener
        $(document).off(".ns-mouse");

        //switch on a touch listener
        $(document).on('touchstart.ns-touch', function () { ns.usingTouch(true) });

        return true;
    }
});

//tests:
//ns.usingMouse()
//$('body').hasClass('mouse');

Możesz teraz powiązać / zasubskrybować usingMouse () i usingTouch () i / lub stylizować swój interfejs za pomocą klasy body.mouse . Interfejs będzie się przełączał w tę i z powrotem, gdy tylko zostanie wykryty kursor myszy i po uruchomieniu Touchstart.

Mamy nadzieję, że wkrótce będziemy mieli lepsze opcje od dostawców przeglądarek.


2

Tera-WURFL może poinformować Cię o możliwościach urządzenia odwiedzającego Twoją witrynę , porównując sygnaturę przeglądarki z jej bazą danych. Zobacz, jest za darmo!


1
To nie zadziała w przypadku urządzeń, które mogą mieć lub nie mieć ekranów dotykowych i myszy. Na przykład komputer stacjonarny z systemem Windows może być podłączony do ekranu dotykowego, ale zwykle będzie również wyposażony w mysz, podczas gdy tablet może również działać pod systemem Windows, ale może nie mieć podłączonej myszy.
Jon Gjengset

@Jonhoo Wystarczy założyć, że do systemów operacyjnych typu desktop jest podłączona mysz. W końcu muszą obsługiwać szeroką gamę oprogramowania, które nie zostało opracowane z myślą o ekranie dotykowym.
Gigi,

1
A co z tabletami ze zwykłym systemem Windows 8? Lub Linux? Albo laptopy z systemem Android?
Jon Gjengset

2
@Jonhoo Oczywiście to podejście jest mniej niż optymalne, ale nie ma przenośnego sposobu, aby to wiedzieć (jeszcze). Jeśli masz laptopa z Androidem, po prostu załóż, że obsługuje dotyk. Jeśli ktoś korzysta z tabletu Windows8, po prostu załóżmy, że obsługuje mysz (system operacyjny musi emulować mysz w programach bez obsługi dotykowej).
Gigi

To jest teraz tak przestarzałe, że nie ma już znaczenia.
Inżynier

2

Dlaczego po prostu nie wykryjesz, czy ma zdolność wyczuwania dotyku i / lub reagowania na ruchy myszy?

// This will also return false on
// touch-enabled browsers like Chrome
function has_touch() {
  return !!('ontouchstart' in window);
}

function has_mouse() {
  return !!('onmousemove' in window);
}

4
Ponieważ niektóre przeglądarki (na przykład IE9) zgłaszają, że funkcja istnieje, nawet jeśli nigdy nie zostanie uruchomiona. Uważam, że jest to również „prawidłowe” zachowanie.
Jon Gjengset,

dlaczego miałbyś używać funkcji? po prostu has_touch = 'ontouchstart' in windowwystarczy i tak dalej.
vsync

Cóż, działa przynajmniej w Chrome 47 dla OS X. Zgłaszanie braku ontouchstart.
phreakhead

2

U mnie to zadziałało w podobnej sytuacji. Zasadniczo załóżmy, że użytkownik nie ma myszy, dopóki nie zobaczysz krótkiej serii następujących po sobie ruchów myszą, bez przerywanych naciśnięć myszy lub przesunięć myszy. Niezbyt eleganckie, ale działa.

var mousedown = false;
var mousemovecount = 0;
function onMouseDown(e){
    mousemovecount = 0;
    mousedown = true;
}
function onMouseUp(e){
    mousedown = false;
    mousemovecount = 0;
}
function onMouseMove(e) {
    if(!mousedown) {
        mousemovecount++;
        if(mousemovecount > 5){
            window.removeEventListener('mousemove', onMouseMove, false);
            console.log("mouse moved");
            $('body').addClass('has-mouse');
        }
    } else {
        mousemovecount = 0;
    }
}
window.addEventListener('mousemove', onMouseMove, false);
window.addEventListener('mousedown', onMouseDown, false);
window.addEventListener('mouseup', onMouseUp, false);


0

Jak zauważyli inni, ostateczne wykrycie, czy mają mysz, jest zawodne. W zależności od urządzenia można to łatwo zmienić. Zdecydowanie jest to coś, czego nie można niezawodnie zrobić z logiczną prawdą lub fałszem, przynajmniej w skali dokumentu.

Wydarzenia związane z dotykiem i myszy są ekskluzywne. Może to trochę pomóc w podejmowaniu różnych działań. Problem polega na tym, że zdarzenia dotyku są bliżej zdarzeń myszy w górę / w dół / ruchu, a także wyzwalają zdarzenie kliknięcia.

Z twojego pytania mówisz, że chcesz mieć najechanie na podgląd. Poza tym nie znam żadnych innych szczegółów dotyczących twojego interfejsu. Jestem zakładając, że przy braku myszy chcesz kranu do podglądu, natomiast kliknięcie wykonuje inną czynność ze względu na podgląd hover.

Jeśli tak jest, możesz przyjąć nieco leniwe podejście do wykrywania:

Zdarzenie onclick zawsze będzie poprzedzone zdarzeniem onmouseover za pomocą myszy. Zwróć więc uwagę, że mysz znajduje się nad elementem, który został kliknięty.

Możesz to zrobić za pomocą zdarzenia onmousemove obejmującego cały dokument. Możesz użyć event.targetdo zarejestrowania elementu, na którym znajduje się mysz. Następnie w zdarzeniach onclick możesz sprawdzić, czy mysz faktycznie znajduje się nad klikanym elementem (lub elementem podrzędnym elementu).

Stamtąd możesz zdecydować, czy chcesz polegać na zdarzeniu kliknięcia dla obu i wykonać akcję A lub B w zależności od wyniku. Akcja B mogłaby być niczym, gdyby niektóre urządzenia dotykowe nie emitowały zdarzenia kliknięcia (zamiast tego musiałbyś polegać na zdarzeniach ontouch *).


0

Nie sądzę, aby można było zidentyfikować urządzenie dotykowe (oczywiście zgodnie z moją wiedzą). Głównym problemem jest to, że wszystkie zdarzenia myszy i klawiatury są również uruchamiane przez urządzenia dotykowe. Zobacz poniższy przykład, oba alerty zwracają wartość true dla urządzeń dotykowych.

function is_touch_present() {
  return ('ontouchstart' in window) || ('onmsgesturechange' in window);
}

function is_mouse_present() {
  return (('onmousedown' in window) && ('onmouseup' in window) && ('onmousemove' in window) && ('onclick' in window) && ('ondblclick' in window) && ('onmousemove' in window) && ('onmouseover' in window) && ('onmouseout' in window) && ('oncontextmenu' in window));
}

alert("Touch Present: " + is_touch_present());
alert("Mouse Present: " + is_mouse_present());

2
Safari ipad powraca truedo'onmousedown' in window
vsync

0

Moim zdaniem najlepszym pomysłem jest mousemovesłuchacz (obecnie najlepsza odpowiedź). Uważam, że tę metodę należy nieco poprawić. Prawdą jest, że przeglądarki dotykowe naśladują nawet zdarzenie mousemove, jak widać w tej dyskusji na temat iOS , więc powinniśmy być trochę ostrożni.

Ma sens, że przeglądarki dotykowe będą emulować to zdarzenie tylko wtedy, gdy użytkownik dotknie ekranu (palec użytkownika jest opuszczony). Oznacza to, że powinniśmy dodać test podczas naszej obsługi ruchu myszy, aby zobaczyć, który przycisk myszy jest wciśnięty (jeśli w ogóle) podczas zdarzenia. Jeśli żaden przycisk myszy nie jest wciśnięty, możemy bezpiecznie założyć, że obecna jest prawdziwa mysz. Jeśli przycisk myszy nie działa, test nie daje jednoznacznych wyników.

Jak więc miałoby to zostać wdrożone? To pytanie pokazuje, że najbardziej niezawodną metodą sprawdzenia, który przycisk myszy jest wciśnięty podczas ruchu myszy, jest faktycznie nasłuchiwanie 3 zdarzeń na poziomie dokumentu: przesunięcie myszy, przesunięcie myszy i przesunięcie myszy. W górę iw dół ustawia tylko globalną flagę logiczną. Ruch wykona test. Jeśli masz ruch, a wartość logiczna jest fałszywa, możemy założyć, że obecna jest mysz. Zobacz pytanie, aby uzyskać dokładne przykłady kodu.

Ostatnia uwaga. Ten test nie jest idealny, ponieważ nie można go wykonać w czasie ładowania. Dlatego użyłbym metody progresywnego ulepszania, jak sugerowano wcześniej. Domyślnie pokazuje wersję, która nie obsługuje interfejsu dymka specyficznego dla myszy. Jeśli mysz zostanie wykryta, włącz ten tryb w środowisku wykonawczym za pomocą JS. Powinno to wyglądać tak płynnie, jak to tylko możliwe dla użytkownika.

W celu obsługi zmian w konfiguracji użytkownika (tj. Mysz została odłączona), możesz okresowo przeprowadzać testy ponownie. Chociaż uważam, że w tym przypadku lepiej będzie po prostu powiadomić użytkownika o 2 trybach i pozwolić użytkownikom ręcznie przełączać się między nimi (podobnie jak wybór mobilny / komputer stacjonarny, który zawsze można odwrócić).


Dzięki za dobre sugestie obejścia ... Myślę, że główny problem nie został rozwiązany, będę musiał skorzystać z jednego z nich
Samuel Rossille

Niestety, ruch myszy uruchamia się po kliknięciu na iPadzie. Testowane tylko z symulatorem. Dla hasMouse () używałem if (! ('Ontouchstart' w oknie)) return true; ale nie działa na laptopach z obsługą dotykową.
Chris Gunawardena,

@Chris G - iPad hell ... (waląc głową w ścianę)
vsync,

0

Przeprowadziłem kilka testów na różnych komputerach PC, Linuksie, iPhonie, telefonach z Androidem i kartach. Dziwne, że nie ma łatwego, kuloodpornego rozwiązania. Problem pojawia się, gdy niektóre urządzenia, które mają dotyk i nie mają myszy, nadal prezentują zdarzenia dotyku i myszy w aplikacji. Ponieważ chcesz obsługiwać instancje tylko myszą i tylko dotykowe, chcesz przetwarzać oba, ale to powoduje podwójne wystąpienie interakcji użytkownika. Jeśli może wiedzieć, że na urządzeniu nie ma myszy, może wiedzieć, że należy ignorować fałszywe / wstawione zdarzenia myszy. Próbowano ustawić flagę w przypadku napotkania MouseMove, ale niektóre przeglądarki wyświetlają fałszywe MouseMove, a także MouseUp i MouseDown. Próbowałem sprawdzić sygnatury czasowe, ale doszedłem do wniosku, że jest to zbyt ryzykowne. Podsumowując: znalazłem przeglądarki, które tworzyły fałszywe zdarzenia myszy, zawsze wstawiały pojedynczy MouseMove tuż przed wstawionym MouseDown. W 99,99% moich przypadków podczas pracy w systemie z prawdziwą myszą występuje wiele następujących po sobie zdarzeń MouseMove - co najmniej dwa. Więc śledź, czy system napotyka dwa kolejne zdarzenia MouseMove i deklaruj, że nie ma myszy, jeśli ten warunek nigdy nie zostanie spełniony. To prawdopodobnie zbyt proste, ale działa na wszystkich moich konfiguracjach testowych. Myślę, że będę się tego trzymać, dopóki nie znajdę lepszego rozwiązania. - Jim W.


0

Proste rozwiązanie w jQuery do wykrywania użycia myszy, które rozwiązuje problem, w którym urządzenia mobilne również wyzwalają zdarzenie „mousemove”. Po prostu dodaj nasłuchiwanie Touchstart, aby usunąć nasłuchiwanie myszy, aby nie było uruchamiane przy dotknięciu.

$('body').one('touchstart.test', function(e) {
  // Remove the mousemove listener for touch devices
  $('body').off('mousemove.test');
}).one('mousemove.test', function(e) {
  // MOUSE!
});

Oczywiście urządzenie nadal może być dotykowe i myszką, ale powyższe zagwarantuje, że została użyta prawdziwa mysz.


0

Właśnie znalazłem rozwiązanie, które moim zdaniem jest dość eleganckie.

// flag as mouse interaction by default
var isMouse = true;

// detect a touch event start and flag it
$(window).on('touchstart', function () {
  // if triggers touchstart, toggle flag
  // since touch events come before mouse event / click
  // callback of mouse event will get in callback
  // `isMouse === false`
  isMouse = false;
});

// now the code that you want to write
// should also work with `mouse*` events
$('a.someClass').on('click', function () {
  if (isMouse) {
    // interaction with mouse event
    // ...
  } else {
    // reset for the next event detection
    // this is crucial for devices that have both
    // touch screen and mouse
    isMouse = true;

    // interaction with touch event
    // ...
  }
});

0

Napotkałem ten sam problem, w którym pojedynczy dotyk był również rejestrowany jako kliknięcie. Po przeczytaniu komentarzy do najczęściej głosowanych odpowiedzi znalazłem własne rozwiązanie:

var body = document.getElementsByTagName('body')[0];
var mouseCount = 0;

// start in an undefined state 
// (i use this to blend in elements once we decide what input is used)
var interactionMode = 'undefined';


var registerMouse = function() {
  // count up mouseCount every time, the mousemove event is triggered
  mouseCount++;

  // but dont set it instantly. 
  // instead wait 20 miliseconds (seems to be a good value for multiple move actions), 
  // if another mousemove event accoures switch to mouse as interaction 
  setTimeout(function() {
    // a touch event triggers also exactly 1 mouse move event.
    // So only if mouseCount is higher than 1 we are really moving the cursor by mouse.
    if (mouseCount > 1) {
      body.removeEventListener('mousemove', registerMouse);
      body.removeEventListener('touchend', registerTouch);

      interactionMode = 'mouse';
      console.log('now mousing');
      listenTouch();
    }

    // set the counter to zero again
    mouseCount = 0;
  }, 20);
};

var registerTouch = function() {
  body.removeEventListener('mousemove', registerMouse);
  body.removeEventListener('touchend', registerTouch);

  interactionMode = 'touch';
  console.log('now touching');
  mouseCount = 0;

  listenMouse();
};

var listenMouse = function() {
  body.addEventListener("mousemove", registerMouse);
};
var listenTouch = function() {
  body.addEventListener("touchend", registerTouch);
};

listenMouse();
listenTouch();

// after one second without input, assume, that we are touching
// could be adjusted to several seconds or deleted
// without this, the interactionMode would stay 'undefined' until first mouse or touch event
setTimeout(function() {
  if (!body.classList.contains('mousing') || body.classList.contains('touching')) {
    registerTouch();
  }
}, 1000);
/* fix, so that scrolling is possible */

html,
body {
  height: 110%;
}
Mouse or touch me

Jedynym problemem, jaki znalazłem, jest to, że musisz mieć możliwość przewijania, aby poprawnie wykryć zdarzenie dotykowe. pojedyncza zakładka (dotyk) może powodować problemy.


0

Spędziłem godziny na rozwiązywaniu tego problemu w mojej aplikacji Phonegap i wymyśliłem ten hack. Generuje ostrzeżenie konsoli, jeśli wyzwolone zdarzenie jest zdarzeniem „pasywnym”, co oznacza, że ​​nie uruchamia żadnej zmiany, ale działa! Byłbym zainteresowany wszelkimi pomysłami na ulepszenie lub lepszą metodę. Ale to skutecznie pozwala mi uniwersalnie używać $ .touch ().

$(document).ready(function(){
  $("#aButton").touch(function(origElement, origEvent){
    console.log('Original target ID: ' + $(origEvent.target).attr('id'));
  });
});

$.fn.touch = function (callback) {
    var touch = false;
    $(this).on("click", function(e){
        if (!touch)
        {
            console.log("I click!");
            let callbackReal = callback.bind(this);
            callbackReal(this, e);
        }else{
            touch = true;
        }
        touch = false;
    });
    $(this).on("touchstart", function(e){
        if (typeof e.touches != typeof undefined)
        {
            e.preventDefault();
            touch = true;
            console.log("I touch!");
            let callbackReal = callback.bind(this);
            callbackReal(this, e);
        }
    });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<button id="aButton">A Button</button>


0

główny problem, jaki tutaj widzę, to fakt, że większość urządzeń dotykowych uruchamia zdarzenie myszy wraz z odpowiadającym mu zdarzeniem dotykowym (touchstart -> ruch myszy, ruch myszy -> ruch myszy, itp.). W przypadku klawiatury tylko i wreszcie dla nowoczesnych mają ogólną przeglądarkę, więc nie można nawet wykryć obecności klasy MouseEvent.

Mniej bolesnym rozwiązaniem byłoby, moim zdaniem, wyświetlenie menu przy uruchomieniu (z zarządzaniem `` alt '' dla użytkowników tylko z klawiaturą) i być może przechowywanie wyboru w localStorage / cookies / serveride lub pozostawienie tego samego wyboru w następnym gdy gość przyjdzie.


-4

Zdecydowanie odradzam takie podejście. Weź pod uwagę ekrany dotykowe, urządzenia wielkości komputera stacjonarnego, a będziesz musiał rozwiązać inny zestaw problemów.

Spraw, aby aplikacja działała bez myszy (tj. Bez podglądu po najechaniu myszą).


1
Dokładnie to próbuję zrobić. Staram się stworzyć interfejs, który będzie działał jak najlepiej na obu tabletach (bez myszy) i za pomocą myszy, ale te interfejsy są z konieczności bardzo różne.
Jon Gjengset

Zgadzam się z broady. Najlepiej jest używać wykrywania urządzeń (np. DeviceAtlas) i wybierać oferowany interfejs w czasie ładowania.
Teemu Ikonen

-4

Chciałbym polecić skrypt, który mi pomógł:

Przeczytałem i wypróbowałem wszystko, co zasugerowałem, bez wystarczających rezultatów.

Następnie zbadałem trochę więcej i znalazłem ten kod - device.js

Używam tego w witrynie mojego klienta, aby wykryć obecność myszy:
( <html>powinien mieć desktopklasę) i wydaje się całkiem niezły, a dla touchwsparcia po prostu regularnie sprawdzam 'ontouchend' in documenti używam informacji z obu wykryć, aby założyć określoną rzecz, której potrzebuję.


Jest to odpowiednik wąchania UA, wielokrotnie poruszanego w tym temacie. Nie rozwiązuje przypadku urządzeń z myszą ORAZ dotykiem, takich jak Windows 8, i zdecydowanie nie jest przyszłościowy.
Hugo Silva

Używam go do rozwiązania tego DOKŁADNEGO przypadku, o którym wspomniałeś w aplikacji klienta i działa dobrze. jest przyszłościowy, ponieważ jest utrzymywany przez jego programistę.
vsync

2
Przypadek, o którym wspomniałem (laptop z obsługą dotykową) zostałby zidentyfikowany przez Twój skrypt jako „Pulpit”, mimo że nie mogłem używać myszy. „to jest przyszłościowe, ponieważ jest utrzymywane przez jego programistę” - myślę, że całkowicie przegapiłeś punkt „przyszłościowego zabezpieczenia”. A gdybyś przeczytał i wypróbował wszystko, jak powiedziałeś, zauważyłbyś, że odpowiedź Gigi już sugeruje wąchanie UA.
Hugo Silva

-7

Generalnie lepszym pomysłem jest wykrycie, czy funkcja przesuwania myszy jest obsługiwana, niż wykrywanie typu systemu operacyjnego / przeglądarki. Możesz to zrobić, wykonując następujące czynności:

if (element.mouseover) {
    //Supports the mouseover event
}

Upewnij się, że nie wykonujesz następujących czynności:

if (element.mouseover()) {
    // Supports the mouseover event
}

Ten ostatni faktycznie wywołałby metodę, zamiast sprawdzać jej istnienie.

Możesz przeczytać więcej tutaj: http://www.quirksmode.org/js/support.html


2
Naprawdę nie wiem, co uzyskać z twojego postu, ale jeśli ('onmouseover' w $ ('body') [0]) alert ('onmouseover'); wyświetla wiadomość również w iPhonie
nraynaud

1
To sprawdza tylko, czy zdefiniowano funkcję mouseover, która byłaby dostępna w prawie wszystkich przeglądarkach. Nie wykrywa, czy mysz jest rzeczywiście obecna.
Jon Gjengset
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.