При зависании на iPad / iPhone пользователь дважды щелкает ссылку - PullRequest
118 голосов
/ 14 июня 2010

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

есть ли способ решить эту проблему? может быть, команда jquery, которую я должен использовать вместо mouseover / out и т.д .. спасибо!

Ответы [ 24 ]

197 голосов
/ 06 августа 2010

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

$('a').on('click touchend', function(e) {
    var el = $(this);
    var link = el.attr('href');
    window.location = link;
});

Идея состоит в том, что Mobile WebKit запускает событие touchend вконец касания, поэтому мы слушаем это и затем перенаправляем браузер, как только по ссылке запускается событие touchend.

36 голосов
/ 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

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

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

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

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

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

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

нет никаких действий

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

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

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

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

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

Вот и все!

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

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

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

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

Дополнительная литература

См. Также Проблема двойного щелчка на iPad / iPhone и Отключение эффектов наведения в мобильных браузерах .

20 голосов
/ 23 мая 2012

Кажется, в конце концов, есть решение CSS. Причина, по которой Safari ждет второго касания, заключается в том, что фоновое изображение (или элементы) обычно назначается для события: hover. Если нет ничего, чтобы показать - у вас не будет никаких проблем. Решение состоит в том, чтобы нацелить платформу iOS с помощью вторичного файла CSS (или стиля в случае подхода JS), который переопределяет: например, наведите курсор на фон, чтобы наследовать и скрыть элементы, которые вы собирались отобразить при наведении мыши:

Вот пример CSS и HTML - блок продукта с помеченной звездочкой над мышью:

HTML:

<a href="#" class="s"><span class="s-star"></span>Some text here</a>

CSS:

.s {
   background: url(some-image.png) no-repeat 0 0;

}
.s:hover {
   background: url(some-image-r.png) no-repeat 0 0;
}

.s-star {
   background: url(star.png) no-repeat 0 0;
   height: 56px;
   position: absolute;
   width: 72px;
   display:none;
}

.s:hover .s-star {
   display:block;
}

Решение (вторичный CSS):

/* CSS */
/* Keep hovers the same or hidden */
.s:hover {
   background:inherit;
}
.s:hover .s-star {
   display:none;
}
5 голосов
/ 24 октября 2013

Что сработало для меня, это то, что другие здесь уже сказали:

Не показывать / скрывать элементы при наведении или перемещении мыши (что в моем случае происходит).

Вот что говорит Apple (https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html):

Кликабельный элемент - это ссылка, элемент формы, область карты изображения или любой другой элемент с обработчиками mousemove, mousedown, mouseup или onclick

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

Таким образом, вы можете использовать решение @ woop: определить userAgent, проверить его и устройство iOS, а затем связать событие. В итоге я использовал эту технику, потому что она соответствует моим потребностям и имеет больше смысла - не связывать события зависания, когда вы этого не хотите.

Но ... если вы не хотите связываться с userAgents и по-прежнему скрывать / показывать элементы в hover / mousemove, я обнаружил, что вы можете сделать это, используя собственный javascript, например:

$("body").on("mouseover", function() {
       document.getElementsByTagName("my-class")[0].style.display = 'block'; //show element
       document.querySelector(".my-selector div").style.display = 'none'; // hide element
});

Это будет работать в настольной версии и ничего не будет делать в мобильной версии.

И для большей совместимости ...

$("body").on("mouseover", function() {
   if (document.getElementsByTagName && document.querySelector) { // check compatibility
       document.getElementsByTagName("my-class")[0].style.display = 'block'; //show element
       document.querySelector(".my-selector div").style.display = 'none'; // hide element
    } else {
        $(".my-class").show();
        $(".my-selector div").hide();
    }
});
3 голосов
/ 21 февраля 2017

Не нужно делать слишком сложным.

$('a').on('touchend', function() {
    $(this).click();
});
3 голосов
/ 21 июля 2012

Решение cduruk было довольно эффективным, но вызывало проблемы в некоторых частях моего сайта.Поскольку я уже использовал jQuery для добавления класса наведения CSS, самым простым решением было просто не добавлять класс наведения CSS на мобильных устройствах (или, точнее, ТОЛЬКО добавлять его, когда НЕ на мобильном устройстве).

Здесь была общая идея:

var device = navigator.userAgent.toLowerCase();
var ios = device.match(/(iphone|ipod|ipad)/);

if (!(ios)) {
    $(".portfolio-style").hover(
        function(){
            $(this).stop().animate({opacity: 1}, 100);
            $(this).addClass("portfolio-red-text");
        },
        function(){
            $(this).stop().animate({opacity: 0.85}, 100);
            $(this).removeClass("portfolio-red-text");
        }
    );
}

* код сокращен для иллюстративных целей

2 голосов
/ 06 августа 2010

Думаю, было бы разумно попробовать mouseenter вместо mouseover.Это то, что используется внутри при связывании с .hover(fn,fn) и обычно то, что вы хотите.

1 голос
/ 06 августа 2010

Я "думаю", что в ваших ссылках нет события onmouseover, когда 1 нажатие активирует onmouseover, а двойное нажатие активирует ссылку но идк. У меня нет iPad. Я думаю, тебе нужно использовать жесты / прикосновения.

https://developer.apple.com/documentation/webkitjs

1 голос
/ 30 сентября 2014

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

Проблемы, которые это решает

Использование только touchstart или touchend:

  • Вызывает событие, когда люди пытаются прокрутить содержимое и просто случайно коснулись этого элемента, когда начинают смахивать - неожиданно вызывая действие.
  • Может вызвать событие при длинном нажатии , аналогично щелчку правой кнопкой мыши на рабочем столе.Например, если событие щелчка переходит к URL-адресу X, и пользователь нажимает кнопку, чтобы открыть X на новой вкладке, пользователь будет смущен, обнаружив X открытым на обеих вкладках.В некоторых браузерах (например, iPhone) это может даже препятствовать появлению длинного меню.

Запуск mouseover событий на touchstart и mouseout на touchmove имеет менее серьезные последствия, номешает обычному поведению браузера, например:

  • Длительное нажатие вызывает запуск мыши, который никогда не заканчивается.
  • Многие браузеры Android обрабатывают положение пальца на touchstartкак mouseover, то есть mouseout ed на следующем touchstart.Таким образом, один из способов просмотра контента при наведении курсора на Android состоит в том, чтобы коснуться интересующей области и пошевелить пальцем, слегка прокручивая страницу.Обработка touchmove как mouseout нарушает это.

Решение

Теоретически, вы можете просто добавить флаг с touchmove, но айфоны запускают сенсорное движение, даже если нетдвижение.Теоретически, вы можете просто сравнить touchstart и touchend событие pageX и pageY , но на iPhone нет touchend pageX или pageY.

Так что, к сожалению, охватить все базы немного сложнее.

$el.on('touchstart', function(e){
    $el.data('tstartE', e);
    if(event.originalEvent.targetTouches){
        // store values, not reference, since touch obj will change
        var touch = e.originalEvent.targetTouches[0];
        $el.data('tstartT',{ clientX: touch.clientX, clientY: touch.clientY } );
    }
});
$el.on('touchmove', function(e){
    if(event.originalEvent.targetTouches){
        $el.data('tstartM', event.originalEvent.targetTouches[0]);
    }
});

$el.on('click touchend', function(e){
    var oldE = $el.data('tstartE');
    if( oldE && oldE.timeStamp + 1000 < e.timeStamp ) {
        $el.data('tstartE',false);
        return;
    }
    if( $el.data('iosTouchM') && $el.data('tstartT') ){
        var start = $el.data('tstartT'), end = $el.data('tstartM');
        if( start.clientX != end.clientX || start.clientY != end.clientY ){
            $el.data('tstartT', false);
            $el.data('tstartM', false);
            $el.data('tstartE',false);
            return;
        }
    }
    $el.data('tstartE',false);

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

1 голос
/ 26 января 2015

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

ПРОБЛЕМА - применение touchend означает, что каждый раз, когда вы прокручиваете палец на элементе, он реагирует так, как если бы вы нажали его, даже если вы просто пытались прокрутить мимо.

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

Вот кнопки:

<a class="menu_button" href="#">
    <div class="menu_underline"></div>
</a>

Я хочу, чтобы div "menu_underline" исчезал при наведении мыши и исчезал при наведении мыши. НО я хочу, чтобы сенсорные устройства могли переходить по ссылке одним кликом, а не двумя.

РЕШЕНИЕ - Вот jQuery, чтобы заставить его работать:

//Mouse Enter
$('.menu_button').bind('touchstart mouseenter', function(){
    $(this).find(".menu_underline").fadeIn();
});

//Mouse Out   
$('.menu_button').bind('mouseleave touchmove click', function(){
    $(this).find(".menu_underline").fadeOut();
});

Большое спасибо за вашу помощь в этом MacFreak.

...