Обнаружение, что браузер не имеет мыши и только для сенсорного - PullRequest
137 голосов
/ 20 октября 2011

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

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

Ответы [ 24 ]

58 голосов
/ 01 августа 2013

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

  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 .. черт!

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

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

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

56 голосов
/ 09 февраля 2012

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

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

(где listen и unlisten делегируют addEventListener или attachEvent, в зависимости от ситуации.)

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

23 голосов
/ 27 февраля 2014

@ Ответ Уайетта великолепен и дает нам много над чем задуматься.

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

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

  1. touchstart
  2. touchmove
  3. touchend
  4. mouseover
  5. mousemove
  6. mousedown
  7. mouseup
  8. click

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

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

16 голосов
/ 17 октября 2018

Начиная с 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
}

Связанные:

Официальный рабочий проект: https://drafts.csswg.org/mediaqueries/#mf-interaction

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

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

9 голосов
/ 03 августа 2013

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

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

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

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 голосов
/ 16 мая 2015

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

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

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

6 голосов
/ 14 февраля 2012

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

4 голосов
/ 02 августа 2013

@ 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');

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

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

2 голосов
/ 19 мая 2016

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

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);
2 голосов
/ 15 февраля 2012

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

// 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...