проблема с наведением iPad / iPhone заставляет пользователя дважды щелкнуть ссылку

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

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

23 ответов


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

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

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


не совсем ясно, что ваш вопрос, но если вы просто хотите устранить двойной щелчок, сохраняя при этом эффект наведения для мыши, мой совет:

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

фон

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

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

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

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

Решение Объяснили

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

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

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

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

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

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

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

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

вот именно!

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

альтернатива Решения

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

  • обнаружение браузера: чрезвычайно склонны к ошибкам. Предполагает, что устройство имеет мышь или касание, в то время как сочетание обоих будет становиться все более распространенным, когда сенсорные дисплеи prolifirate.
  • css media detection: единственное CSS-единственное решение, о котором я знаю. Все еще подвержены ошибкам, и по-прежнему предполагает, что устройство имеет мышь или касание, пока оба возможны.
  • эмулировать событие click в touchend: это будет неправильно следовать по ссылке, даже если пользователь хотел только прокручивать или масштабировать, без намерения фактически щелкнуть ссылку.
  • используйте переменную для подавления событий мыши: это установить переменную в touchend это используется как условие if в последующих событиях мыши, чтобы предотвратить изменения состояния в этот момент времени. Переменная сбрасывается в событие click. Это достойное решение, если вы действительно не хотите эффекта наведения на сенсорные интерфейсы. К сожалению, это не работает, если touchend запускается по другой причине, и событие click не запускается (например, пользователь прокручивается или масштабируется), а затем пытается следовать по ссылке с помощью мыши (i.e на устройстве с помощью мыши и сенсорного интерфейса).

Более Дальнеишее Чтение

см. также iPad / iPhone двойной щелчок проблема и отключить эффекты наведения на мобильный браузеры.


кажется, что есть решение 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;
}

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

не показывать/скрывать элементы при наведении или mousemove (что является событием в моем случае).

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

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

если пользователь нажимает кликабельный элемент, события поступают в следующем порядке: mouseover, mousemove, mousedown, mouseup и click. Кроме того,если содержимое страницы изменяется в событии mousemove, последующие события в последовательности не отправляются. это поведение позволяет пользователю нажать в новом содержимом.

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

но... если вы не хотите возиться с userAgents и все еще скрывать / показывать элементы при наведении / 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();
    }
});

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

вот общая идея:

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");
        }
    );
}

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


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


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

проблемы это решает

используя touchstart или touchend:

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

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

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

решение

теоретически, вы можете просто добавить флаг с touchmove, но айфоны вызывают touchmove, даже если нет движения. Теоретически, вы можете просто сравнить touchstart и touchend событие pageX и pageY но на айфонах нет 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);

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


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


Я только что узнал, что он работает, если вы добавляете пустой слушатель, не спрашивайте меня, почему, но я тестировал его на iPhone и iPad с iOS 9.3.2, и он работал нормально.

if(/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream){
    var elements = document.getElementsByTagName('a');
    for(var i = 0; i < elements.length; i++){
        elements[i].addEventListener('touchend',function(){});
    }
}

нет необходимости усложнять.

$('a').on('touchend', function() {
    $(this).click();
});

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

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


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

однако, мое решение было: На событии щелчка (или касании? вы устанавливаете таймер. Если ссылка нажата снова в течение X МС, вы просто возвращаете false.

чтобы установить таймер для каждого элемента, вы можете использовать $.data().

Это также может исправить проблему @Ferdy, описанную выше.


Если вы используете Modernizr, то очень легко использовать Modernizr.коснитесь, как упоминалось ранее.

однако я предпочитаю использовать комбинацию Modernizr.touch и тестирование агента пользователя, просто для безопасности.

var deviceAgent = navigator.userAgent.toLowerCase();

var isTouchDevice = Modernizr.touch || 
(deviceAgent.match(/(iphone|ipod|ipad)/) ||
deviceAgent.match(/(android)/)  || 
deviceAgent.match(/(iemobile)/) || 
deviceAgent.match(/iphone/i) || 
deviceAgent.match(/ipad/i) || 
deviceAgent.match(/ipod/i) || 
deviceAgent.match(/blackberry/i) || 
deviceAgent.match(/bada/i));

function Tipsy(element, options) {
    this.$element = $(element);
    this.options = options;
    this.enabled = !isTouchDevice;
    this.fixTitle();
};

Если вы не используете Modernizr, вы можете просто заменить


можно использовать click touchend ,

пример:

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

пример выше повлияет на все ссылки на сенсорных устройствах.

если вы хотите нацелить только определенные ссылки, вы можете сделать это, установив класс на них, т. е.:

HTML:

<a href="example.html" class="prevent-extra-click">Prevent extra click on touch device</a>

Jquery:

$('a.prevent-extra-click').on('click touchend', function() {
    var linkToAffect = $(this);
    var linkToAffectHref = linkToAffect.attr('href');
    window.location = linkToAffectHref;
});

спасибо,

Йерун


я столкнулся с аналогичной ситуацией, когда у меня были события, привязанные к состояниям mouseenter/mouseleave/click элемента, но на iPhone пользователю пришлось дважды щелкнуть элемент, чтобы сначала вызвать событие mouseenter, а затем снова запустить событие click.

Я решил это с помощью аналогичного метода, как указано выше, но я использовал jQuery $.плагин браузера (для jQuery 1.9>) и добавлен.событие триггера для события привязки mouseenter выглядит следующим образом:

// mouseenter event
$('.element').on( "mouseenter", function() {
    // insert mouseenter events below

    // double click fix for iOS and mouseenter events
    if ($.browser.iphone || $.browser.ipad) $(this).trigger('click');
});
// mouseleave event
$('.element').on( "mouseleave", function() { 
    // insert mouseout events below
});
// onclick event
$('.element').on( "click", function() {
    // insert click events below
});

в .триггер предотвращает необходимость двойного щелчка по элементу путем запуска .нажмите обработчик событий на mouseenter (или начальный щелчок) элемента при просмотре на iPhones или iPads. Возможно, это не самое элегантное решение, но оно отлично работает в моем случае и использует плагин, который у меня уже был, и потребовал от меня добавить одну строку кода, чтобы мои существующие события работали под этими устройствами.

вы можете получить jQuery $.плагин браузера здесь: https://github.com/gabceb/jquery-browser-plugin


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

// tablet "one touch (click)" X "hover" > link redirection
$('a').on('touchmove touchend', function(e) {

    // if touchmove>touchend, set the data() for this element to true. then leave touchmove & let touchend fail(data=true) redirection
    if (e.type == 'touchmove') {
        $.data(this, "touchmove_cancel_redirection", true );
        return;
    }

    // if it's a simple touchend, data() for this element doesn't exist.
    if (e.type == 'touchend' && !$.data(this, "touchmove_cancel_redirection")) {
        var el = $(this);
        var link = el.attr('href');
        window.location = link;
    }

    // if touchmove>touchend, to be redirected on a future simple touchend for this element
    $.data(this, "touchmove_cancel_redirection", false );
});

С вдохновением от MacFreak, я собрал то, что работает для меня.

этот метод JS предотвращает наведение на ipad и предотвращает регистрацию щелчка как два щелчка в некоторых случаях. В CSS, если у вас есть :наведите классы psudo в css, измените их на .например, наведите курсор .некоторые-класс: наведите курсор .некоторый класс.парить

проверьте этот код на ipad, чтобы увидеть, как css и метод наведения JS ведут себя по-разному (только в эффекте наведения). Кнопка CSS не фантазии пункт предупреждение. http://jsfiddle.net/bensontrent/ctgr6stm/

function clicker(id, doStuff) {
  id.on('touchstart', function(e) {
    id.addClass('hover');
  }).on('touchmove', function(e) {
    id.removeClass('hover');
  }).mouseenter(function(e) {
    id.addClass('hover');
  }).mouseleave(function(e) {
    id.removeClass('hover');
  }).click(function(e) {
    id.removeClass('hover');
    //It's clicked. Do Something
    doStuff(id);
  });
}

function doStuff(id) {
  //Do Stuff
  $('#clicked-alert').fadeIn(function() {
    $(this).fadeOut();
  });
}
clicker($('#unique-id'), doStuff);
button {
  display: block;
  margin: 20px;
  padding: 10px;
  -webkit-appearance: none;
  touch-action: manipulation;
}
.hover {
  background: yellow;
}
.btn:active {
  background: red;
}
.cssonly:hover {
  background: yellow;
}
.cssonly:active {
  background: red;
}
#clicked-alert {
  display: none;
}
<button id="unique-id" class="btn">JS Hover for Mobile devices<span id="clicked-alert"> Clicked</span>

</button>
<button class="cssonly">CSS Only Button</button>
<br>This js method prevents hover from sticking on an ipad, and prevents the click registering as two clicks. In CSS, if you have any :hover in your css, change them to .hover For example .some-class:hover to .some-class.hover

чтобы ссылки работали без нарушения прокрутки касания, я решил это с помощью события " tap " jQuery Mobile:

    $('a').not('nav.navbar a').on("tap", function () {
        var link = $(this).attr('href');
        if (typeof link !== 'undefined') {
            window.location = link;
        }
    });

Я слишком поздно, я знаю, но это один из самых простых временное решение я нашел:

    $('body').on('touchstart','*',function(){   //listen to touch
        var jQueryElement=$(this);  
        var element = jQueryElement.get(0); // find tapped HTML element
        if(!element.click){
            var eventObj = document.createEvent('MouseEvents');
            eventObj.initEvent('click',true,true);
            element.dispatchEvent(eventObj);
        }
    });

это работает не только для ссылок(якорных тегов), но и для других элементов. Надеюсь, это поможет.


этот короткий фрагмент, кажется, работает. Запуск события click при нажатии ссылки:

  $('a').on('touchstart', function() {
    $(this).trigger('click');
  });

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

Я использую этот:

var selector = "label, a, button";
var timer;
var startX;
var startY;
$(document).on("click", selector, function (e) {
    if ($(this).data("touched") === true) {
        e.stopImmediatePropagation();
        return false;
    }
    return;
}).on("touchend", selector, function (e) {
    if (Math.abs(startX - e.originalEvent.changedTouches[0].screenX) > 10 || Math.abs(startY - e.originalEvent.changedTouches[0].screenY) > 10)
        // user action is not a tap
        return;
    var $this = $(this);
    // Visit: http://stackoverflow.com/questions/1694595/can-i-call-jquery-click-to-follow-an-a-link-if-i-havent-bound-an-event-hand/12801548#12801548
    this.click();
    // prevents double click
    $this.data("touched", true);
    if (timer)
        clearTimeout(timer);
    setTimeout(function () {
        $this.data("touched", false);
    }, 400);
    e.stopImmediatePropagation();
    return false;
}).on("touchstart", function (e) {
    startX = e.originalEvent.changedTouches[0].screenX;
    startY = e.originalEvent.changedTouches[0].screenY;
});

вы можете проверить navigator.userAgent такой:

if(!navigator.userAgent.match(/iPhone/i) || !navigator.userAgent.match(/iPad/i)) {
    //bind your mouseovers...
}

но вы должны проверить для ежевики, дроидов, набор других сенсорных устройств. Вы также можете привязать mouseovers, только если userAgent содержит Mozilla, IE, Webkit или Opera, но вам все равно нужно просмотреть некоторые устройства, потому что Droid, например, сообщает свою строку userAgent как:

Mozilla/5.0 (Linux; U; Android 2.0.1; en-us; Droid Build/ESD56) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17

строка iPhone аналогична. Если вы просто экран для iPhone, iPod, iPad, Android и Blackberry вы большинство карманных компьютеров можно получить, но не все.


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

@media screen and (min-device-width:1024px) {
    your-element:hover {
        /* Do whatever here */
    }
}

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

<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />