Подтвердить что ты не робот

Отключение эффектов зависания на мобильных браузерах

Я пишу веб-сайт, предназначенный для использования как на рабочих столах, так и на планшетах. Когда он посещается с рабочего стола, я хочу, чтобы экраны с интерактивными экранами загорались с эффектами :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.

4b9b3361

Ответ 1

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

  • Добавьте эффекты наведения на 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

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

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

Решение объяснено

Подведем итог:

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

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

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

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

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

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

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

Это!

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

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

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

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

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

Ответ 2

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

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

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

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

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

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

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

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

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

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

Ответ 3

Я написал следующий 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();

Ответ 4

Мое решение - добавить hover-active класс css в тег 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;
}

Ответ 5

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

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

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

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

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

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

Ответ 6

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

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

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

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

Ответ 7

В моем проекте мы решили эту проблему, используя 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);
   });

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-started/browsers-devices/#sticky-hoverfocus-on-mobile

Ответ 8

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

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

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

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

       #buttonOne{
            height: 44px;
        }


        #buttonOne:hover{
            display:none;
        }
}   

Ответ 9

Iv'd нашел 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

ИЗМЕНИТЬ

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

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

Ответ 10

Вы можете запускать событие 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);
    }
}

Ответ 11

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

Ознакомьтесь с последним примером здесь: http://api.jquery.com/mouseover/, вы можете использовать что-то похожее на журнал, когда событие срабатывает и отбирает его отсюда!

Ответ 12

Если вы счастливы использовать JavaScript, вы можете использовать Modernizr на своей странице. Когда страница загружается, браузер с сенсорным экраном будет иметь добавленный к тегу html класс ".no-touch", но для браузера с сенсорным экраном в теге html будет добавлен класс ".touch" в тег html,

Тогда это просто случай проверки того, имеет ли тег html класс без касания, прежде чем принимать решение о добавлении слушателей mouseenter и mouseleave.

if($('html').hasClass('no-touch')){
    $('.box').on("mouseenter", function(event){
            $(this).css('background-color','#0000ff')
    });
    $('.box').on("mouseleave", function(event){
            $(this).css('background-color','')
    });
}

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

Ответ 13

В проекте, который я недавно сделал, я решил эту проблему с функцией делегированных событий jQuery. Он ищет определенные элементы, используя селектор jQuery, и добавляет/удаляет класс CSS для этих элементов, когда мышь находится над элементом. Кажется, это хорошо работает, насколько я смог проверить его, в том числе IE10 на ноутбуке с сенсорным управлением под Windows 8.

$(document).ready(
    function()
    {
        // insert your own selector here: maybe '.hoverable'?
        var selector = 'button, .hotspot';

        $('body')
            .on('mouseover', selector, function(){ $(this).addClass('mouseover');    })
            .on('mouseout',  selector, function(){ $(this).removeClass('mouseover'); })
            .on('click',     selector, function(){ $(this).removeClass('mouseover'); });
    }
);

edit: Это решение, конечно же, требует, чтобы вы изменили свой CSS, чтобы удалить селектор ": hover", и заранее подумайте о том, какие элементы вы хотите "нависнуть".

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

Ответ 14

Вот мое решение: http://jsfiddle.net/agamemnus/g56aw709/-- ниже.

Все, что нужно сделать, - это преобразовать их ": hover" в ".hover"... это! Большая разница между этим и остальным заключается в том, что это также будет работать на селекторах несингулярных элементов, таких как .my_class > *:hover {.

handle_css_hover_effects ()

function handle_css_hover_effects (init) {
 var init = init || {}
 var handle_touch_events = init.handle_touch_events || true
 var handle_mouse_events = init.handle_mouse_events || true
 var hover_class         = init.hover_class         || "hover"
 var delay_preferences   = init.delay_preferences   || {touch: {add: 500, remove: 500}}
 function default_handler (curobj, input_type, op) {
  var hovered_element_selector = "*" + ((op == "add") ? ":" : ("." + hover_class))
  var hovered_elements = Array.prototype.slice.call(document.body.querySelectorAll(hovered_element_selector))
  var modified_list = []
  while (true) {
   if ((curobj == null) || (curobj == document.documentElement)) break
   if (hovered_elements.indexOf(curobj) != -1) modified_list.push (curobj)
   curobj = curobj.parentNode
  }
  function do_hover_change () {modified_list.forEach (function (curobj) {curobj.classList[op](hover_class)})}
  if ((!delay_preferences[input_type]) || (!delay_preferences[input_type][op])) {
   do_hover_change ()
  } else {
   setTimeout (do_hover_change, delay_preferences[input_type][op])
  }
 }

 if (handle_mouse_events) {
  document.body.addEventListener ('mouseover' , function (evt) {var curobj = evt.target; default_handler (curobj, "mouse", "add")})
  document.body.addEventListener ('mouseout'  , function (evt) {var curobj = evt.target; default_handler (curobj, "mouse", "remove")})
  document.body.addEventListener ('click'     , function (evt) {var curobj = evt.target; default_handler (curobj, "mouse", "remove")})
 }

 if (handle_touch_events) {
  document.body.addEventListener ('touchstart', function (evt) {var curobj = evt.target; default_handler (curobj, "touch", "add")})
  document.body.addEventListener ('touchend'  , function (evt) {var curobj = evt.target; default_handler (curobj, "touch", "remove")})
  document.body.addEventListener ('touchmove',  function (evt) {
   var curobj = evt.target
   var hovered_elements = Array.prototype.slice.call(document.body.querySelectorAll("*:hover"))
   var lastobj = null
   evt = evt.changedTouches[0]
   var elements_at_point = get_elements_at_point (evt.pageX, evt.pageY)
   // Get the last element that isn't at the current point but is still hovered over, and remove only its hover attribute.
   while (true) {
    if ((curobj == null) || (curobj == document.documentElement)) break
    if ((hovered_elements.indexOf(curobj) != -1) && (elements_at_point.indexOf(curobj) == -1)) lastobj = curobj
    curobj = curobj.parentNode
   }
   if (lastobj == null) return
   if ((!delay_preferences.touch) || (!delay_preferences.touch.remove)) {
    lastobj.classList.remove(hover_class)
   } else {
    setTimeout (function () {lastobj.classList.remove(hover_class)}, delay_preferences.touch.remove)
   }

   function get_elements_at_point (x, y) {
    var el_list = [], pe_list = []
    while (true) {
     var curobj = document.elementFromPoint(x, y)
     if ((curobj == null) || (curobj == document.documentElement)) break
     el_list.push (curobj); pe_list.push (curobj.style.pointerEvents)
     curobj.style.pointerEvents = "none"
    }
    el_list.forEach (function (current_element, i) {current_element.style.pointerEvents = pe_list[i]})
    return el_list
   }
  })
 }
}

Ответ 15

Включите Modernizr на своей странице и установите вместо этого свои состояния наведения:

html.no-touchevents .box:hover {
    background: blue;
}

Ответ 16

Привет, человек из будущего, вы, вероятно, захотите использовать медиа-запрос pointer и/или hover. Запрос handheld был устаревшим.

/* device is using a mouse or similar */
@media (pointer: fine) {
  a:hover {
    background: red;
  }
}

Ответ 17

Попробуйте это простое решение jquery 2019 года, хотя оно уже давно;

  1. добавить этот плагин в голову:

    src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"

  2. добавить это к JS:

    $("*").on("touchend", function(e) { $(this).focus(); });//applies to all elements

  3. некоторые предлагаемые варианты этого:

    $(":input, :checkbox,").on("touchend", function(e) {(this).focus);}); //specify elements
    
    $("*").on("click, touchend", function(e) { $(this).focus(); });  //include click event'
    
    css: body { cursor: pointer; } //touch anywhere to end a focus'
    

Заметки

  • Поместите плагин перед bootstrap.js, если это применимо, чтобы избежать влияния на всплывающие подсказки.
  • протестировано только на iphone XR ios 12.1.12 и ipad 3 ios 9.3.5 с использованием Safari или Chrome.

Рекомендации:

https://code.jquery.com/ui/

https://api.jquery.com/category/selectors/jquery-selector-extensions/