Отключить эффекты наведения на мобильные браузеры - PullRequest
110 голосов
/ 28 ноября 2011

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

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

Как получить эффекты наведения при использовании мыши, но подавить их при использовании сенсорного экрана?

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

Я уже пытался перехватить события touchstart, touchmove и touchend и вызвать preventDefault() для всех из них, что иногда подавляет «невидимый курсор мыши»; но если я быстро нажму вперед и назад между двумя различными элементами, после нескольких нажатий он начнет перемещать «курсор мыши» и, в любом случае, подсвечивать эффекты наведения - это как будто мой preventDefault не всегда соблюдается. Я не буду утомлять вас деталями без необходимости - я даже не уверен, что это правильный подход; если у кого-то есть более простое решение, я весь в ушах.


Редактировать: Это может быть воспроизведено с помощью стандартного болотного CSS :hover, но вот краткое повторение для справки.

<style>
  .box { border: 1px solid black; width: 150px; height: 150px; }
  .box:hover { background: blue; }
</style>
<div class="box"></div>
<div class="box"></div>

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

Я также опубликовал пример здесь , который выполняет вышеприведенное, а также перехватывает события мыши jQuery. Вы можете использовать его, чтобы увидеть, что события касания также будут запускать mouseenter, mousemove и mouseleave.

Ответы [ 17 ]

82 голосов
/ 17 марта 2014

Я понял из вашего вопроса, что ваш эффект при наведении меняет содержимое вашей страницы.В этом случае мой совет:

  • Добавить эффекты наведения на touchstart и mouseenter.
  • Удалить эффекты наведения на mouseleave, touchmove и click.

Кроме того, вы можете отредактировать страницу, не изменяя содержимого.

Фон

Чтобы имитировать мышь, браузеры, такие как мобильный Webkit, запускают следующие события, если пользователь касается и отпускает палец на сенсорном экране (например, iPad) (источник: Touch And Mouse на html5rocks.com):

  1. touchstart
  2. touchmove
  3. touchend
  4. 300 мс задержка, когда браузер проверяет, что это одно нажатие, а недвойное касание
  5. mouseover
  6. mouseenter
    • Примечание : если изменяется событие mouseover, mouseenter или mousemoveсодержимое страницы, следующие события никогда не запускаются.
  7. mousemove
  8. mousedown
  9. mouseup
  10. click

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

Что еще хуже, если событие mouseover изменяет содержимое страницы, событие click никогда не запускается, как описано в Safari Web Content Guide -Обработка событий , в частности рисунок 6.4 в Событиях одним пальцем .Что именно означает «изменение контента», будет зависеть от браузера и версии.Я обнаружил, что для iOS 7.0 изменение цвета фона не является (или больше не?) Изменением содержимого.

Объяснение решения

Напомним:

  • Добавление эффектов наведения на touchstart и mouseenter.
  • Удаление эффектов наведения на mouseleave, touchmove и click.

Обратите внимание, что нетдействие на touchend!

Это явно работает для событий мыши: mouseenter и mouseleave (слегка улучшенные версии mouseover и mouseout) запускаются, а также добавляют и удаляют наведение.

Если пользователь на самом деле click sa ссылается, эффект наведения также удаляется.Это гарантирует, что оно будет удалено, если пользователь нажмет кнопку «Назад» в веб-браузере.

Это также работает для сенсорных событий: при сенсорном запуске добавляется эффект наведения мыши.Это "не" удалено на touchend.Он снова добавляется в mouseenter, и, поскольку это не вызывает изменений содержимого (оно уже было добавлено), также запускается событие click, и по ссылке следует переходить без необходимости повторного нажатия пользователем!

Задержка в 300 мс, которую браузер имеет между событием touchstart и click, фактически используется надлежащим образом, поскольку эффект наведения будет отображаться в течение этого короткого времени.

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

Вот и все!

Обратите внимание, что можно удалить задержку в 300 мс, например, с помощью Библиотека FastClick , но это выходит за рамки этого вопроса.

Альтернативные решения

Я обнаружил следующие проблемы со следующими альтернативами:

  • Обнаружение браузера: Чрезвычайно подвержен ошибкам.Предполагается, что на устройстве есть либо мышь, либо сенсорный экран, а комбинация обоих будет становиться все более распространенной, когда сенсорные дисплеи будут распространяться.
  • Обнаружение мультимедиа в CSS: Единственное решение, предназначенное только для CSS I 'Я в курсе.По-прежнему подвержен ошибкам и по-прежнему предполагает, что на устройстве есть либо мышь, либо сенсорный экран, хотя возможны оба варианта.
  • Эмулировать событие щелчка в touchend: Это будет неправильно следовать ссылке,даже если пользователь только хотел прокрутить или увеличить масштаб, не намереваясь фактически нажать на ссылку.
  • Используйте переменную для подавления событий мыши: Устанавливает переменную в touchend, которая используется как условие if в последующих событиях мыши, чтобы предотвратить изменения состояния в этот момент времени. Переменная сбрасывается в событии щелчка. Смотрите ответ Уолтера Романа на этой странице. Это достойное решение, если вы действительно не хотите, чтобы на сенсорных интерфейсах был эффект наведения. К сожалению, это не работает, если touchend запускается по другой причине, и не происходит событие щелчка (например, пользователь прокручивал или масштабировал) и впоследствии пытается перейти по ссылке с помощью мыши (то есть на устройстве с обеими мышами). и сенсорный интерфейс).

Дальнейшее чтение

19 голосов
/ 28 ноября 2011

Как получить эффекты наведения при использовании мыши, но подавить их, когда я использую сенсорный экран?

Может быть, не думаю об этом слишком многокак подавление эффектов наведения для сенсорных экранов, но как добавление эффектов наведения для событий мыши?

Если вы хотите сохранить эффекты :hover в своем CSS, вы можете указать разные стили для разных медиа:

@media screen { /* hover styles here */ } 

@media handheld { /* non-hover styles here */ }

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

@media screen and (max-width:800px) { /* non-hover styles here */ }

Так что, даже если «экранная» или «портативная» часть игнорируется, «максимальная ширина» поможет вамвы.Вы можете просто предположить, что все, что имеет экран меньше 800 пикселей, должно быть планшетом или телефоном и не использовать эффекты наведения.Для тех редких пользователей, которые используют мышь на устройстве с низким разрешением, они не увидят эффект парения, но в противном случае ваш сайт будет в порядке.

Дальнейшее чтение медиазапросов?Есть много статей об этом в Интернете - вот одна из них: http://www.alistapart.com/articles/return-of-the-mobile-stylesheet

Если вы переместите эффекты наведения из вашего CSS и примените их с помощью JavaScript, то вы можете привязать конкретно к событиям мыши и / или сноваВы могли бы просто сделать некоторые предположения, основанные только на размере экрана, с наихудшей «проблемой», заключающейся в том, что какой-то пользователь, использующий мышь, пропускает эффекты при наведении.

9 голосов
/ 14 марта 2014

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

Модуль mobileNoHoverState ниже имеет переменнуюpreventMouseover (первоначально объявленный как false), который устанавливается в true, когда пользователь запускает событие touchstart для элемента, $target.

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

Мы знаем, что mouseover запускается после touchstart из-за порядка, в котором они объявляются в init.

var mobileNoHoverState = function() {

    var hoverClass = 'hover',
        $target = $(".foo"), 
        preventMouseover = false;

    function forTouchstart() {
        preventMouseover = true;
    }

    function forMouseover() {
        if (preventMouseover === false) {
            $(this).addClass(hoverClass);
        } else {
            preventMouseover = false;
        }
    }

    function forMouseout() {
        $(this).removeClass(hoverClass);
    }

    function init() {
        $target.on({
            touchstart  : forTouchstart,
            mouseover   : forMouseover,
            mouseout    : forMouseout
        });                
    }

    return {
        init: init
    };
}();

Затем создается экземпляр модуля ниже:

mobileNoHoverState.init();
5 голосов
/ 10 ноября 2016

Я действительно хотел получить чистое css решение для этого, так как разбрасывать весомое решение javascript вокруг всех моих взглядов казалось неприятным вариантом. Наконец нашел запрос @media.hover, который может определить «позволяет ли первичный механизм ввода пользователю наводить курсор на элементы». Это позволяет избежать сенсорных устройств, в которых «зависание» является скорее эмулируемым действием, чем прямой возможностью устройства ввода.

Так, например, если у меня есть ссылка:

<a href="/" class="link">Home</a>

Тогда я могу смело присвоить ему стиль :hover только тогда, когда устройство легко его поддерживает с помощью этого css:

@media (hover: hover) {
  .link:hover { /* hover styles */ }
}

В то время как большинство современных браузеров поддерживают запросы функций интерактивных медиа, некоторые популярные браузеры , такие как IE и Firefox , не поддерживают. В моем случае это работает нормально, так как я собирался поддерживать Chrome только для настольных компьютеров, а также Chrome и Safari для мобильных устройств.

5 голосов
/ 25 августа 2014

Мое решение - добавить класс HTML hover-active к тегу HTML, и использовать его в начале всех селекторов CSS с: hover и удалите этот класс в первом событии сенсорного запуска.

http://codepen.io/Bnaya/pen/EoJlb

JS:

(function () {
    'use strict';

    if (!('addEventListener' in window)) {
        return;
    }

    var htmlElement = document.querySelector('html');

    function touchStart () {
        document.querySelector('html').classList.remove('hover-active');

        htmlElement.removeEventListener('touchstart', touchStart);
    }

    htmlElement.addEventListener('touchstart', touchStart);
}());

HTML:

<html class="hover-active">

CSS:

.hover-active .mybutton:hover {
    box-shadow: 1px 1px 1px #000;
}
2 голосов
/ 28 ноября 2011

То, что я сделал для решения той же проблемы, - это обнаружение функции (я использую что-то вроде этот код ), проверяя, определен ли onTouchMove, и если да, то добавляю класс css "touchMode""к телу, иначе я добавлю" desktopMode ".

Тогда каждый раз, когда какой-либо стиль применяется только к сенсорному устройству или только к рабочему столу, к правилу css добавляется соответствующий класс:

.desktopMode .someClass:hover{ color: red }
.touchMode .mainDiv { width: 100%; margin: 0; /*etc.*/ }

Редактировать : Конечно, эта стратегия добавляет несколько дополнительных символов к вашему CSS, поэтому если вас беспокоит размер CSS, вы можете найти определения touchMode и desktopMode и поместить их в разныефайлы, так что вы можете подать оптимизированный CSS для каждого типа устройства;или вы можете изменить имена классов на что-то намного более короткое, прежде чем переходить к продукту.

2 голосов
/ 24 мая 2017

В моем проекте мы решили эту проблему, используя https://www.npmjs.com/package/postcss-hover-prefix и https://modernizr.com/ Сначала мы постобработаем выходные файлы css с postcss-hover-prefix. Он добавляет .no-touch для всех правил CSS hover.

const fs = require("fs");
const postcss = require("postcss");
const hoverPrfx = require("postcss-hover-prefix");

var css = fs.readFileSync(cssFileName, "utf8").toString();
postcss()
   .use(hoverPrfx("no-touch"))
   .process(css)
   .then((result) => {
      fs.writeFileSync(cssFileName, result);
   });

1011 * CSS *

a.text-primary:hover {
  color: #62686d;
}

становится

.no-touch a.text-primary:hover {
  color: #62686d;
}

Во время выполнения Modernizr автоматически добавляет классы CSS в тег html, подобный этому

<html class="wpfe-full-height js flexbox flexboxlegacy canvas canvastext webgl 
  no-touch 
  geolocation postmessage websqldatabase indexeddb hashchange
  history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage
  borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients
  cssreflections csstransforms csstransforms3d csstransitions fontface
  generatedcontent video audio localstorage sessionstorage webworkers
  applicationcache svg inlinesvg smil svgclippaths websocketsbinary">

Такая пост-обработка css plus Modernizr отключает наведение для сенсорных устройств и позволяет другим. Фактически, этот подход был вдохновлен Bootstrap 4, и они решили ту же проблему: https://v4 -alpha.getbootstrap.com / Getting-Start / Browsers-devices / # sticky-hoverfocus-on-mobile

1 голос
/ 09 июля 2015

Вы можете вызвать событие mouseLeave всякий раз, когда вы касаетесь элемента на сенсорном экране.Вот решение для всех <a> тегов:

function removeHover() {
    var anchors = document.getElementsByTagName('a');
    for(i=0; i<anchors.length; i++) {
        anchors[i].addEventListener('touchstart', function(e){
            $('a').mouseleave();
        }, false);
    }
}
1 голос
/ 21 января 2015

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

Это хорошо, но не поддерживается очень хорошо:

html.touch *:hover {
    all:unset!important;
}

Но это очень хорошая поддержка :

html.touch *:hover {
    pointer-events: none !important;
}

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

Обнаружение касания от устройств без сенсорного ввода, я думаю, Modernizr сделал лучшую работу:

https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touchevents.js

EDIT

Я нашел лучшее и более простое решение этой проблемы

Как определить, является ли клиент сенсорным устройством

1 голос
/ 31 июля 2013

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

Мне в основном пришлось взять огромное приложение, которое кто-то сделал, и сделать его отзывчивым. Они использовали jQueryUI и попросили меня не вмешиваться ни в один из их jQuery, поэтому я ограничился использованием только CSS.

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

@media only screen and (max-width:1024px) {

       #buttonOne{
            height: 44px;
        }


        #buttonOne:hover{
            display:none;
        }
}   
...