Обнаружение, что браузер не имеет мыши и только для сенсорного

149

Я разрабатываю веб-приложение (не веб-сайт со страницами с интересным текстом) с совершенно другим интерфейсом для касания (ваш палец скрывает экран при нажатии) и мыши (в значительной степени зависит от предварительного просмотра при наведении курсора). Как я могу обнаружить, что у моего пользователя нет мыши, чтобы предоставить ему правильный интерфейс? Я планирую оставить переключатель для людей с мышью и сенсорным (как некоторые ноутбуки).

Возможность сенсорного события в браузере на самом деле не означает, что пользователь использует сенсорное устройство (например, Modernizr его не отключает). Код, который правильно отвечает на вопрос, должен возвращать false, если на устройстве есть мышь, в противном случае - true. Для устройств с мышью и сенсорным экраном он должен возвращать false (не только сенсорный)

В качестве примечания, мой сенсорный интерфейс может также подойти для устройств только с клавиатурой, так что я скорее хочу обнаружить отсутствие мыши.

Чтобы прояснить необходимость, вот API, который я ищу для реализации:

// 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);
nraynaud
источник
Я думаю, что вам нужно переосмыслить свой дизайн, если вы хотите, чтобы одно приложение было применимо как для настольных компьютеров, так и для мобильных устройств / сенсорных устройств, но для каждого из них характерны разные варианты поведения. Я не думаю, что то, что вам нужно, на самом деле возможно в данный момент, так как быстрый поиск в Google по запросу «javascript Detective Mouse» показывает один умеренно полезный пост на quirksmode.org для обнаружения различных состояний мыши (щелчки, положение, и т. д.), но ZERO показывает, существует ли мышь на самом деле.
davethegr8
24
Может быть, это потому, что Google не помог, что я спросил это здесь.
nraynaud
2
Вы пробовали документ mouseenter из jquery? $ (document) .mouseenter (function (e) {alert ("mouse");});
Параг Гаджар
4
После рассмотрения почти дюжины многообещающих путей только для того, чтобы отвергнуть каждый из них в течение нескольких минут, этот вопрос вызывает у меня совершенно безумные помешательства.
Джордан Грей

Ответы:

65

Основная проблема заключается в том, что у вас есть следующие различные классы устройств / вариантов использования:

  1. Мышь и клавиатура (рабочий стол)
  2. Только касание (телефон / планшет)
  3. Мышь, клавиатура и тач (сенсорные ноутбуки)
  4. Сенсорный и клавиатура (блютуз клавиатура на планшете)
  5. Только мышь (отключенный пользователь / предпочтения просмотра)
  6. Только клавиатура (отключенный пользователь / настройки просмотра)
  7. Сенсорный экран и мышь (то есть при наведении курсора на ручку Galaxy Note 2)

Хуже всего то, что можно переходить от некоторых из этих классов к другим (подключать мышь, подключать к клавиатуре), или пользователь может ВИДЕТЬ, что он находится на обычном ноутбуке, пока не дотянется и не коснется экрана.

Вы правы, предполагая, что присутствие конструкторов событий в браузере не является хорошим способом для продвижения вперед (и это несколько противоречиво). Кроме того, если вы не отслеживаете очень конкретное событие или не пытаетесь исключить только несколько классов выше, использование самих событий не является полным доказательством.

Например, скажем, вы обнаружили, что пользователь испустил реальное движение мыши (не ложное от событий касания, см. Http://www.html5rocks.com/en/mobile/touchandmouse/ ).

Тогда что?

Вы включаете стили наведения? Вы добавляете больше кнопок?

В любом случае вы увеличиваете время на стекло, потому что вам нужно ждать, пока событие сработает.

Но что произойдет, когда ваш благородный пользователь решит отключить свою мышь и полностью прикоснуться к ней ... Вы ждете, пока он коснется вашего теперь переполненного интерфейса, а затем измените его сразу после того, как он попытался точно определить ваш теперь переполненный пользовательский интерфейс?

В виде маркера, цитируя штукатурку на https://github.com/Modernizr/Modernizr/issues/869#issuecomment-15264101

  • Мы хотим обнаружить присутствие мыши
  • Ae, вероятно, не может обнаружить, прежде чем событие происходит
  • Таким образом, мы обнаруживаем, что в этом сеансе была использована мышь - она ​​не будет сразу же после загрузки страницы.
  • Мы, вероятно, также не можем обнаружить, что мыши нет - она ​​будет неопределенной до истины (я думаю, что это имеет больше смысла, чем установка в ложь, пока не доказано)
  • И мы, вероятно, не можем определить, отключена ли мышь в середине сеанса - это будет неразличимо, если пользователь просто сдастся с помощью мыши.

Кроме того: браузер знает, когда пользователь подключает мышь / подключается к клавиатуре, но не открывает ее для JavaScript ... черт!

Это должно привести вас к следующему:

Отслеживание текущих возможностей данного пользователя является сложным, ненадежным и сомнительным достоинством

Идея прогрессивного улучшения здесь вполне применима. Создайте опыт, который работает гладко, независимо от контекста пользователя. Затем сделайте предположения, основанные на функциях браузера / медиазапросах, чтобы добавить функциональность, которая будет относительной в предполагаемом контексте. Присутствие мыши - это лишь один из множества способов, которыми разные пользователи на разных устройствах воспринимают ваш сайт. Создайте что-нибудь с достоинствами в своем ядре и не беспокойтесь о том, как люди нажимают на кнопки.

Wyatt
источник
3
отличный ответ. Надеюсь, у пользователя всегда есть экран! Я думаю, что имеет смысл создать интерфейс, который бы соответствовал текущему режиму взаимодействия пользователя. На сенсорном ноутбуке имеет смысл настроить приложение (то есть :hoverэлементы и тому подобное), когда пользователь переключается с мыши на сенсорное. Кажется маловероятным, что пользователь в настоящее время использует мышь + касание в одно и то же время (я имею в виду comoonn, это как подключить 2 мыши к одному и тому же компьютеру, хахаха)
Себастьен Лорбер,
@SebastienLorber - очень не хочется вас ломать, но у пользователей не всегда есть экран. ( Можно ли использовать javascript для определения того, работает ли программа чтения с экрана на компьютере пользователя? )
ashleedawg
57

Как насчет прослушивания события mousemove в документе. Затем, пока вы не услышите это событие, вы предполагаете, что устройство сенсорное или только клавиатурное.

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

(Где listenи unlistenделегировать addEventListenerили по attachEventмере необходимости.)

Надеюсь, это не приведет к слишком большому визуальному рывку, это будет отстой, если вам понадобятся массивные повторные макеты, основанные на режиме ...

Дэн
источник
2
Это хорошая идея, но, к сожалению, задержка в ответе сделает ее непригодной для использования, когда пользовательский интерфейс приложения зависит от того, доступна ли мышь. Это особенно верно, если приложение может быть встроено, поэтому события мыши будут попадать на него, только если мышь движется над самим iframe ..
Джон Гьенсет
7
Это может работать, если приложение запускается с заставкой и кнопкой «Продолжить». Если мышь перемещается до первого события mousedown, значит, у вас есть мышь. Сбой возможен только в том случае, если кнопка загружена непосредственно под мышкой, а у пользователя очень устойчивая рука (нужно даже поднять 1 движущийся пиксель).
SpliFF
49
хорошая идея, но , похоже, не работает в нашем тестировании . iPad запускает это событие.
Джефф Этвуд
3
@JeffAtwood, что ты сделал в своем деле?
Майкл Харен
2
iPad точно вызывает событие mousemove, прямо перед событием mousedown. Я обнаружил, что mousedown count> 0 и mousedown count == mousemove count являются хорошим способом обнаружить отсутствие мыши. Я не могу продублировать это настоящей мышью.
Питер Вустер
37

Начиная с 2018 года, существует хороший и надежный способ определить, есть ли в браузере мышь (или подобное устройство ввода): функции взаимодействия с мультимедиа в CSS4, которые теперь поддерживаются практически любым современным браузером (кроме IE 11 и специальных мобильных браузеров).

W3C:

Функция указателя мультимедиа используется для запроса наличия и точности указательного устройства, такого как мышь.

Смотрите следующие варианты:

    /* 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) { ... }

Медиа-запросы также могут быть использованы в JS:

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

Связанный:

Документация W3: https://www.w3.org/TR/mediaqueries-4/#mf-interaction

Поддержка браузера: https://caniuse.com/#search=media%20features

Аналогичная проблема: определить, поддерживает ли клиентское устройство состояния: hover и: focus

Blackbam
источник
Мне лично нравится этот ответ, но на данный момент (10/19) CSS-запросы @media hover и pointer доступны только на ~ 85% устройств по всему миру, согласно caniuse.com. Конечно, неплохо, 95% или выше предпочтительнее. Надеюсь, это скоро станет стандартом на устройствах.
MQuiggGeorgia
1
@MQuiggGeorgia В принципе, я согласен с вашей критикой, она пока не везде поддерживается. Тем не менее caniuse.com для меня говорит, что поддерживается 91,2% ( caniuse.com/#feat=css-media-interaction ). При более близком рассмотрении он поддерживается везде, кроме IE 11 и специальных плоских браузеров на мобильных устройствах. Чтобы быть справедливым, это справедливо для любой современной функции, так как Microsoft давно прекратила реализацию функций IE. Для IE 11 вы можете использовать отступление от других ответов здесь.
Blackbam
23

Ответ @ Wyatt великолепен и дает нам много о чем подумать.

В моем случае я предпочел прислушиваться к первому взаимодействию, чтобы только потом установить поведение. Поэтому, даже если у пользователя есть мышь, я буду воспринимать ее как сенсорное устройство, если первое взаимодействие было касанием.

Учитывая данный порядок, в котором обрабатываются события :

  1. touchstart
  2. TouchMove
  3. touchend
  4. Mouseover
  5. MouseMove
  6. MouseDown
  7. MouseUp
  8. щелчок

Можно предположить, что если событие мыши запускается до касания, это настоящее событие мыши, а не эмуляция. Пример (с использованием 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);
});

Это сработало для меня

Хьюго Силва
источник
У меня не работает, Ipad Safari (IOS8.3) также обнаруживает мышь с этим фрагментом
netzaffin
3
@netzaffin. Спасибо за отзыв, я нашел более последовательным использование mousedown вместо наведения мыши. Не могли бы вы взглянуть на эту скрипку со своего IOS и сообщить мне результат? Приветствия jsfiddle.net/bkwb0qen/15/embedded/result
Уго Сильва
1
Если у вас сенсорный экран с мышью, будет обнаружен только метод ввода, использованный первым.
0xcaff
11

Можно только определить, поддерживает ли браузер сенсорный ввод . Нет способа узнать, подключен ли к нему сенсорный экран или мышь.

Можно расставить приоритеты использования, хотя, прослушивая событие касания вместо события мыши, если обнаружена возможность касания.

Чтобы обнаружить сенсорные возможности кросс-браузера:

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

Затем можно использовать это, чтобы проверить:

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

или выбрать событие для прослушивания:

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

или используйте отдельные подходы для сенсорного или не сенсорного:

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
}

Для мыши можно только определить, используется ли мышь, а не существует она или нет. Можно установить глобальный флаг, чтобы указать, что мышь была обнаружена при использовании (аналогично существующему ответу, но немного упрощенному):

var hasMouse = false;

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

(нельзя включить mouseupили, mousedownпоскольку эти события также могут быть вызваны касанием)

Браузеры ограничивают доступ к низкоуровневым системным API-интерфейсам, которые необходимы для обнаружения таких функций, как аппаратные возможности системы, в которой они используются.

Существует возможность, возможно, написать плагин / расширение для доступа к ним, но через JavaScript и DOM такое обнаружение ограничено для этой цели, и нужно будет написать плагин, специфичный для различных платформ ОС.

Итак, в заключение: такое обнаружение может быть оценено только по «хорошей догадке».


источник
8

Когда Media Queries Level 4 доступен в браузерах, мы сможем использовать запросы «указатель» и «зависание» для обнаружения устройств с помощью мыши.

Если мы действительно хотим передать эту информацию в Javascript, мы могли бы использовать CSS-запрос для установки определенных стилей в соответствии с типом устройства, а затем использовать getComputedStyleв Javascript для чтения этого стиля и извлечения из него исходного типа устройства.

Но мышь может быть подключена или отключена в любое время, и пользователь может захотеть переключаться между касанием и мышью. Поэтому нам может потребоваться обнаружить это изменение и предложить изменить интерфейс или сделать это автоматически.

joeytwiddle
источник
1
В частности, any-pointerиany-hover позволит вам изучить все применимые возможности устройства. Приятно взглянуть на то, как мы могли бы решить эту проблему в будущем! :)
Джордан Грей,
2
window.matchMedia ("(any-pointer: coarse)"). соответствует === верно?
4esn0k
7

Поскольку вы планируете в любом случае предложить способ переключения между интерфейсами, возможно ли будет просто попросить пользователя щелкнуть ссылку или кнопку, чтобы «ввести» правильную версию приложения? Тогда вы могли вспомнить их предпочтения для будущих посещений. Это не хай-тек, но это на 100% надежно :-)

Brandan
источник
2
На самом деле это довольно хорошее предложение, но оно задерживает время, прежде чем пользователь доберется до реального интерфейса. Кроме того, я должен предоставить способ переключения после первоначального выбора. В итоге получается больше работы, чем если бы ее можно было просто обнаружить ..
Джон Гьенсет
1
Запрашиваемая пользователем, безусловно, лучший способ - если не всегда надежный - и дает вам удобное место для размещения обновлений-уведомлений, а что нет. Я думаю, что вы задумались над «проблемой» ..
T4NK3R
4

@SamuelRossille К сожалению, нет браузеров, в которых мне известно о существовании (или его отсутствии) мыши.

Итак, с учетом всего сказанного, мы просто должны постараться и сделать все возможное с нашими существующими опциями ... событиями. Я знаю, что это не совсем то, что вы ищете ... согласился, что это далеко от идеала.

Мы можем сделать все возможное, чтобы выяснить, использует ли пользователь мышь или сенсорный в любой момент. Вот быстрый и грязный пример использования 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');

Теперь вы можете привязать / подписаться на usingMouse () и usingTouch () и / или стилизовать ваш интерфейс с классом body.mouse . Интерфейс будет переключаться вперед и назад, как только будет обнаружен курсор мыши, и при сенсорном запуске.

Надеюсь, у нас скоро появятся лучшие варианты от поставщиков браузеров.

смельчака
источник
2

Tera-WURFL может рассказать вам о возможностях устройства, которое посещает ваш сайт , сравнив сигнатуру браузера с его базой данных. Посмотрите, это бесплатно!

Gigi
источник
1
Это не будет работать для устройств, которые могут иметь или не иметь сенсорные экраны и мышь. Например, настольный компьютер с ОС Windows может быть подключен к сенсорному экрану, но обычно на нем также есть мышь, в то время как на планшете также может работать Windows, но не может быть подключена мышь.
Джон Гьенсет,
@Jonhoo Просто предположим, что настольные операционные системы имеют подключенную мышь. В конце концов, они должны поддерживать широкий спектр программного обеспечения, которое не было разработано с учетом сенсорного экрана.
Гиги
1
Как насчет планшетов под управлением простой Windows 8? Или Linux? Или ноутбуки под управлением Android?
Джон Гьенсет
2
@Jonhoo Очевидно, что этот подход не является оптимальным, но пока нет портативного способа узнать это. Если у вас работает ноутбук с Android, просто предположите, что он поддерживает сенсорный ввод. Если у вас планшет под управлением Windows8, просто предположите, что он поддерживает мышь (ОС должна эмулировать мышь для не сенсорных программ).
Гиги
Это сейчас настолько устарело, что больше не актуально.
инженер
2

Почему бы вам просто не определить, обладает ли он способностью ощущать прикосновения и / или реагировать на движения мыши?

// 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);
}
iblue
источник
4
Потому что некоторые браузеры (например, IE9) сообщают, что функция существует, даже если она никогда не будет запущена. Я считаю, что это также "правильное" поведение.
Джон Гьенсет,
почему вы используете функцию? просто has_touch = 'ontouchstart' in windowхватит и тд.
vsync
Ну, это работает на Chrome 47 для OS X, по крайней мере. Отчетность отсутствует.
phreakhead
2

Это сработало для меня в аналогичной ситуации. По сути, предположим, что у пользователя нет мыши, пока вы не увидите короткую серию последовательных движений мыши, без вмешательства мышами или мышиных мышек. Не очень элегантно, но это работает.

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

Взгляните на моденизр, одной из его особенностей является сенсорный

http://modernizr.com/docs/#features-misc

Пока я не проверил полностью, кажется, работает очень хорошо

Также это связано со страницей modernizr http://www.html5rocks.com/en/mobile/touchandmouse/

exussum
источник
Это пытается определить возможности касания, а не то, есть ли касание ТОЛЬКО и нет возможности мыши. Этот вопрос принципиально отличается от обнаружения касания.
Джимбо Джонни
0

Как указывали другие, окончательное определение наличия у них мыши ненадежно. Это может легко измениться, в зависимости от устройства. Это определенно то, что вы не можете надежно сделать с логическим значением true или false, по крайней мере, в масштабе документа.

События касания и события мыши являются эксклюзивными. Так что это может несколько помочь при совершении различных действий. Проблема в том, что события касания находятся ближе к событиям мыши вверх / вниз / перемещения, а также вызывают событие щелчка.

От вашего вопроса вы говорите, что хотите иметь предварительный просмотр. Кроме того, я не знаю никаких других особенностей вашего интерфейса. Я предполагаю, что при отсутствии мыши вы хотите, чтобы касание предварительно просматривалось, в то время как щелчок выполняет другое действие из-за всплывающего предварительного просмотра.

Если это так, вы можете использовать ленивый подход к обнаружению:

Событию onclick всегда будет предшествовать событие onmouseover с мышью. Поэтому запомните, что мышь находится над элементом, по которому щелкнули.

Вы можете сделать это с помощью события onmousemove для всего документа. Вы можете использовать event.targetдля записи, на каком элементе находится мышь. Затем внутри событий onclick вы можете проверить, находится ли мышь на самом деле над элементом, по которому щелкают (или потомком элемента).

Оттуда вы можете выбрать либо использовать событие click для обоих, и выполнить действие A или B в зависимости от результата. Действие B может быть ничем, если некоторые сенсорные устройства не генерируют событие щелчка (вместо этого вы должны полагаться на события ontouch *).

Люк
источник
0

Я не думаю, что можно идентифицировать устройство только для касания (насколько мне известно, конечно). Основная проблема заключается в том, что все события мыши и клавиатуры запускаются сенсорными устройствами. В следующем примере оба оповещения возвращают значение true для сенсорных устройств.

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());
Bikas
источник
2
Safari Ipad возвращается trueна'onmousedown' in window
VSync
0

На мой взгляд, лучшая идея - это mousemoveслушатель (в настоящее время лучший ответ). Я считаю, что этот метод нужно немного доработать. Это правда, что сенсорные браузеры эмулируют даже событие mousemove, как вы можете видеть в этом обсуждении iOS , поэтому мы должны быть немного осторожнее.

Имеет смысл, что сенсорные браузеры будут эмулировать это событие только тогда, когда пользователь нажимает на экран (палец пользователя опущен). Это означает, что мы должны добавить тест во время нашего обработчика перемещения мыши, чтобы увидеть, какая кнопка мыши нажата (если есть) во время события. Если ни одна кнопка мыши не нажата, мы можем с уверенностью предположить, что настоящая мышь присутствует. Если кнопка мыши нажата, тест остается безрезультатным.

Так как это будет реализовано? Этот вопрос показывает, что самый надежный способ выяснить, какая кнопка мыши нажата во время перемещения мыши, - это фактически прослушивать 3 события на уровне документа: mousemove, mousedown и mouseup. Вверх и вниз будет установлен только глобальный логический флаг. Ход выполнит тест. Если у вас есть ход и логическое значение ложно, мы можем предположить, что мышь присутствует. Смотрите вопрос для точных примеров кода.

Один последний комментарий. Этот тест не идеален, потому что он не может быть выполнен во время загрузки. Поэтому я бы использовал прогрессивный метод улучшения, как было предложено ранее. По умолчанию отображается версия, которая не поддерживает специфический для мыши интерфейс наведения мыши. Если мышь обнаружена, включите этот режим во время выполнения с помощью JS. Это должно выглядеть как можно более незаметно для пользователя.

Чтобы поддержать изменения в конфигурации пользователя (т. Е. Мышь была отключена), вы можете периодически повторно тестировать. Хотя я полагаю, что в этом случае будет лучше просто уведомить пользователя о двух режимах и позволить пользователям вручную переключаться между ними (как, например, выбор мобильного / настольного компьютера, который всегда можно изменить).

talkol
источник
Спасибо за хорошие предложения по обходным путям ... Я думаю, что главная проблема заключается в том, чтобы не быть советом, мне придется прибегнуть к одному из них
Сэмюэлю Россиллю
К сожалению, мышиный ход срабатывает при нажатии на iPad. Только проверено с симулятором. Для hasMouse () я использовал if (! ('Ontouchstart' в окне)) return true; но не работает для сенсорных ноутбуков.
Крис Гунавардена
@Chris G - адский ад ... (стучит головой о стену)
vsync
0

Провел несколько тестов на разных ПК, Linux, iPhone, Android телефонах и вкладках. Странно, что нет простого пуленепробиваемого решения. Проблема возникает, когда некоторые приложения, в которых есть Touch и мышь не по-прежнему представляют события Touch и Mouse в приложении. Поскольку мы хотим поддерживать экземпляры только для мыши и только для касания, хотим обрабатывать оба, но это вызывает двойное возникновение пользовательских взаимодействий. Если может знать, что мышь не присутствует на устройстве, то может знать, чтобы игнорировать ложные / вставленные события мыши. Попытка установки флага, если встречается MouseMove, но некоторые браузеры выдают поддельные MouseMove, а также MouseUp и MouseDown. Пробовал изучать временные метки, но решил, что это слишком рискованно. Итог: я обнаружил, что браузеры, создавшие ложные события мыши, всегда вставляли один MouseMove непосредственно перед вставленным MouseDown. В 99,99% моих случаев при работе в системе с настоящей мышью происходит несколько последовательных событий MouseMove - не менее двух. Поэтому следите за тем, встречает ли система два последовательных события MouseMove, и объявляйте, что мыши нет, если это условие никогда не выполняется. Это, вероятно, слишком просто, но работает на всех моих тестовых установках. Думаю, я буду придерживаться этого, пока не найду лучшее решение. - Джим В.

jwasch
источник
0

Простое решение в jQuery для обнаружения использования мыши, которое решает проблему, при которой мобильные устройства также вызывают событие mousemove. Просто добавьте слушателя сенсорного запуска, чтобы удалить слушателя перемещения мыши, чтобы он не срабатывал при касании.

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

Конечно, устройство все еще может быть сенсорной и мышью, но вышеизложенное гарантирует, что использовалась настоящая мышь.

suncat100
источник
0

Только что нашел решение, которое я считаю довольно элегантным.

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

Я столкнулся с той же самой проблемой, где одно касание было также зарегистрировано как щелчок. После того, как я прочитал комментарии самых популярных ответов, я нашел собственное решение:

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

Единственная проблема, которую я обнаружил, заключается в том, что вы должны иметь возможность прокрутки, чтобы правильно обнаружить сенсорное событие. одна вкладка (касание) может создать проблемы.

Яннис Нексор Янус Бетц
источник
0

Я часами разбирался с этой проблемой для своего приложения Phonegap и придумал этот хак. Он генерирует консольное предупреждение, если вызванное событие является «пассивным» событием, то есть оно не вызывает никаких изменений, но работает! Я был бы заинтересован в любых идеях для улучшения или лучшего метода. Но это эффективно позволяет мне использовать $ .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

Основная проблема, которую я вижу здесь, состоит в том, что большинство сенсорных устройств запускают событие мыши вместе с отвечающим за одно касание ядром (touchstart -> mousedown, touchmove -> mousemove и т. д.). Только для клавиатуры, наконец, для современных, они имеют универсальный браузер, поэтому вы даже не можете обнаружить присутствие класса MouseEvent.

На мой взгляд, менее болезненным решением было бы, по моему мнению, отображать меню при запуске (с управлением «alt» для пользователей только с клавиатуры) и, возможно, сохранить выбор с помощью localStorage / cookies / serveride или же оставить тот же выбор следующим время посетитель приходит

Ernoriel
источник
-4

Я настоятельно рекомендую против такого подхода. Рассмотрите устройства с сенсорным экраном, размером с настольный компьютер, и у вас есть другой набор проблем, которые нужно решить.

Пожалуйста, сделайте ваше приложение пригодным для использования без мыши (т.е. без предварительного просмотра).

Крис Бродфут
источник
1
Это именно то, что я пытаюсь сделать. Я пытаюсь создать интерфейс, который будет работать наилучшим образом как на планшетах (без мыши), так и с помощью мыши, но эти интерфейсы обязательно очень разные.
Джон Гьенсет
Я согласен с Броды. Лучше всего использовать обнаружение устройств (например, DeviceAtlas) и выбирать предлагаемый интерфейс во время загрузки.
Теему Иконен
-4

Хотел бы порекомендовать скрипта, который мне помог:

Я прочитал и попробовал все предложенное, без достаточных результатов.

Тогда я исследовал еще немного и нашел этот код - device.js

Я использую это на веб-сайте моего клиента, чтобы обнаружить существование мыши:
( <html>должен иметь desktopкласс), и это кажется довольно хорошим, и для touchподдержки я просто делаю регулярную проверку 'ontouchend' in documentи использую информацию из обоих обнаружений, чтобы предположить, что мне нужна конкретная вещь.

VSync
источник
Это эквивалентно UA sniffing, который уже много раз поднимался в этой теме. Это не решает проблему устройств с мышью и сенсорным, как Windows 8, и это определенно не будущее.
Уго Сильва
Я использую его для разрешения ТОЧНОГО случая, который вы упомянули в заявке клиента, и он работает хорошо. это будущее, потому что оно поддерживается разработчиком.
vsync
2
Случай, который я упомянул (ноутбук с сенсорным экраном) будет идентифицирован вашим сценарием как «Рабочий стол», хотя я мог не использовать мышь. «Это доказательство будущего, потому что оно поддерживается разработчиком» - я думаю, что вы полностью упустили здесь «будущее». И если бы вы прочитали и попробовали все, как заявили, вы бы заметили, что ответ Джиджи уже предлагает нюхать UA.
Уго Сильва
-7

Как правило, лучше определить, поддерживается ли функция наведения мыши, а не определять тип ОС / браузера. Вы можете сделать это просто следующим образом:

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

Убедитесь, что вы не делаете следующее:

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

Последний на самом деле вызовет метод, а не проверит его существование.

Вы можете прочитать больше здесь: http://www.quirksmode.org/js/support.html

Маркус Своп
источник
2
Я действительно не знаю, что получить от вашего поста, но if ('onmouseover' в $ ('body') [0]) alert ('onmouseover'); также отображает сообщение в iPhone
nraynaud
1
Это только проверяет, определена ли функция наведения мыши, что было бы почти во всех браузерах. Он не определяет наличие мыши.
Джон Гьенсет