Можно ли принудительно игнорировать псевдовектор: hover для пользователей iPhone/iPad?

У меня есть некоторые css-меню на моем сайте, которые расширяются с помощью :hover (без js)

Это работает полуразрушенным способом в iDevices, например, нажатие активирует правило :hover и расширяет меню, но затем нажатие в другом месте не удаляет :hover. Также, если есть ссылка внутри элемента :hover 'ed, вам нужно дважды нажать, чтобы активировать ссылку (первые триггеры срабатывания :hover, вторая клавиша срабатывания триггеров).

Мне удалось заставить все работать на iphone, привязывая событие touchstart.

Проблема в том, что иногда мобильное сафари по-прежнему выбирает запуск правила :hover из css вместо из моих событий touchstart!

Я знаю, что это проблема, потому что когда я отключу все правила :hover вручную в css, мобильное сафари прекрасно работает (но обычные браузеры, очевидно, больше не работают).

Есть ли способ динамически "отменить" правила :hover для определенных элементов, когда пользователь находится на мобильном сафари?

Смотрите и сравните поведение iOS здесь: http://jsfiddle.net/74s35/3/ Обратите внимание: только некоторые свойства css запускают поведение двух кликов, например. Дисплей: нет; но не фон: красный; или текстовое оформление: подчеркивание;

Ответ 1

Я обнаружил, что ": hover" непредсказуем в iPhone/iPad Safari. Иногда tap on element делает этот элемент ": hover" , иногда он дрейфует к другим элементам.

В настоящее время у меня просто класс "без касания" в теле.

<body class="yui3-skin-sam no-touch">
   ...
</body>

И все правила CSS с надписью ": hover" ниже ".no-touch":

.no-touch my:hover{
   color: red;
}

Где-то на странице у меня есть javascript, чтобы удалить класс без прикосновения из тела.

if ('ontouchstart' in document) {
    Y.one('body').removeClass('no-touch');
}

Это не выглядит идеально, но все равно работает.

Ответ 2

:hover здесь не проблема. Safari для iOS следует очень странному правилу. Сначала он запускает mouseover и mousemove; если во время этих событий ничего не изменилось, 'click' и связанные с ним события не срабатывают:

Diagram of touch event in iOS

mouseenter и mouseleave, как представляется, включены, хотя они не указаны в диаграмме.

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

$(window).on('mousemove', function() {
    $('body').attr('rel', Math.random());
});

Изменить: для пояснения событие jQuery hover включает mouseenter и mouseleave. Они предотвратят click, если содержимое будет изменено.

Ответ 3

Библиотека обнаружения функций браузера Модернизатор включает проверку событий касания.

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

Если события касания не включены, Modernizr может добавить класс no-touch:

<html class="no-touch">

И затем разверните стили hover с этим классом:

.no-touch a:hover { /* hover styles here */ }

загрузите собственную сборку Modernizr, чтобы включить в нее как можно меньше или больше функций.

Вот пример некоторых классов, которые могут быть применены:

<html class="js no-touch postmessage history multiplebgs
             boxshadow opacity cssanimations csscolumns cssgradients
             csstransforms csstransitions fontface localstorage sessionstorage
             svg inlinesvg no-blobbuilder blob bloburls download formdata">

Ответ 4

Некоторые устройства (как говорили другие) имеют как события касания, так и мыши. Например, на Microsoft Surface есть сенсорный экран, трекпад и стилус, который фактически поднимает события наведения, когда он висит над экраном.

Любое решение, которое отключает :hover на основе присутствия событий touch, также влияет на пользователей Surface (и многие другие подобные устройства). Многие новые ноутбуки касаются друг друга и будут реагировать на события касания - поэтому отключить зависание - это действительно плохая практика.

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

Мое решение:

Некоторые предложили использовать Modernizr уже, хорошо Modernizr позволяет создавать собственные тесты. То, что я в основном делаю здесь, - это абстрагирование идеи браузера, который поддерживает :hover в тесте Modernizr, который я могу использовать во всем моем коде без жесткого кодирования if (iOS).

 Modernizr.addTest('workinghover', function ()
 {
      // Safari doesn't 'announce' to the world that it behaves badly with :hover
      // so we have to check the userAgent  
      return navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? false : true;
 });

Тогда css становится чем-то вроде этого

html.workinghover .rollover:hover 
{
    // rollover css
}

Только на iOS этот тест завершится с ошибкой и отключит опрокидывание.

Лучшая часть такой абстракции состоит в том, что если я нахожу, что она ломается на определенном андроиде или если она исправлена ​​в iOS9, тогда я могу просто изменить тест.

Ответ 5

Добавление библиотеки FastClick на вашу страницу приведет к превращению всех кранов на мобильном устройстве в события кликов (независимо от того, где пользователь нажимает), поэтому он должен также устранить проблему с зависанием на мобильных устройствах. В качестве примера я отредактировал вашу скрипку: http://jsfiddle.net/FvACN/8/.

Просто включите fastclick.min.js lib на свою страницу и активируйте с помощью:

FastClick.attach(document.body);

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


Есть несколько незначительных последствий для использования FastClick, которые могут или не могут иметь значения для вашего сайта:

  • Если вы нажмете где-нибудь на странице, прокрутите вверх, прокрутите назад, а затем отпустите палец в том же месте, в котором вы его поместили, FastClick интерпретирует это как "click", хотя это явно не так. По крайней мере, как это работает в версии FastClick, которую я сейчас использую (1.0.0). Возможно, кто-то исправил проблему с этой версии.
  • FastClick удаляет возможность для кого-то "дважды щелкнуть".

Ответ 6

В основном три сценария:

  • Пользователь имеет только устройство мыши/указателя и может активировать :hover
  • Пользователь имеет только сенсорный экран и не может активировать элементы :hover
  • Пользователь имеет как сенсорный экран, так и указательное устройство

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

Решение iOS на проблему невозможности наведения на сенсорные устройства (как описано в @Zenexer) является умным, но может привести к тому, что простой код будет ошибочным (как отмечает OP). Отключение зависания только для устройств с сенсорным экраном означает, что вам все равно придется кодировать альтернативу с сенсорным экраном. Обнаружение, когда пользователь имеет как указатель, так и сенсорный экран, дополнительно смешивает воды (как объясняется @Simon_Weaver).

На этом этапе самым безопасным решением является отказ от использования :hover в качестве единственного способа взаимодействия пользователя с вашим сайтом. Эффекты Hover - хороший способ показать, что ссылка или кнопка являются действительными, но пользователю не требуется наведывать элемент для выполнения действий на вашем веб-сайте.

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

  • Замена меню наведения с прямыми действиями (всегда видимые ссылки)
  • Замена наводящих меню с помощью контекстных меню
  • Перемещение большого количества содержимого наведите на отдельную страницу

Двигаясь вперед, это, вероятно, будет лучшим решением для всех новых проектов. Принятый ответ, вероятно, является вторым лучшим решением, но обязательно учитывайте устройства, которые также имеют указательные устройства. Будьте внимательны, чтобы не устранять функциональность, когда устройство имеет сенсорный экран, чтобы обойти iOS :hover hack.

Ответ 8

Версия JQuery в вашем использовании .css.no-touch.my-element: hover для всех правил наведения включают JQuery и следующие script

function removeHoverState(){
    $("body").removeClass("no-touch");
}

Затем в теге body добавьте   class= "no-touch" ontouchstart = "removeHoverState()"

как только ontouchstart запускает класс для всех состояний наведения, удаляется

Ответ 9

Вместо того, чтобы иметь только зависающие эффекты, когда сенсорный доступ недоступен, я создал систему обработки событий касания, и это решило проблему для меня. Во-первых, я определил объект для тестирования событий "tap" (эквивалент "click").

touchTester = 
{
    touchStarted: false
   ,moveLimit:    5
   ,moveCount:    null
   ,isSupported:  'ontouchend' in document

   ,isTap: function(event)
   {
      if (!this.isSupported) {
         return true;
      }

      switch (event.originalEvent.type) {
         case 'touchstart':
            this.touchStarted = true;
            this.moveCount    = 0;
            return false;
         case 'touchmove':
            this.moveCount++;
            this.touchStarted = (this.moveCount <= this.moveLimit);
            return false;
         case 'touchend':
            var isTap         = this.touchStarted;
            this.touchStarted = false;
            return isTap;
         default:
            return true;
      }
   }
};

Затем в моем обработчике событий я делаю что-то вроде следующего:

$('#nav').on('click touchstart touchmove touchend', 'ul > li > a'
            ,function handleClick(event) {
               if (!touchTester.isTap(event)) {
                  return true;
               }

               // touch was click or touch equivalent
               // nromal handling goes here.
            });

Ответ 10

Спасибо @Morgan Cheng за ответ, однако я немного изменил функцию JS для получения "touchstart" (код, взятый из @Timothy Perez answer), хотя для этого вам нужен jQuery 1.7+

  $(document).on({ 'touchstart' : function(){
      //do whatever you want here
    } });

Ответ 11

Учитывая ответ, предоставленный Zenexer, шаблон, который не требует дополнительных HTML-тегов, следующий:

jQuery('a').on('mouseover', function(event) {
    event.preventDefault();
    // Show and hide your drop down nav or other elem
});
jQuery('a').on('click', function(event) {
    if (jQuery(event.target).children('.dropdown').is(':visible') {
        // Hide your dropdown nav here to unstick
    }
});

Этот метод сначала запускает указатель мыши, а затем второй щелчок.

Ответ 12

Просто посмотрите размер экрана....

@media (min-width: 550px) {
    .menu ul li:hover > ul {
    display: block;
}
}

Ответ 13

введите код, который вы хотите поместить в

// a function to parse the user agent string; useful for 
// detecting lots of browsers, not just the iPad.
function checkUserAgent(vs) {
    var pattern = new RegExp(vs, 'i');
    return !!pattern.test(navigator.userAgent);
}
if ( checkUserAgent('iPad') ) {
    // iPad specific stuff here
}