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


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

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

23 114

23 ответа:

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

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

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

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

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

фон

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

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

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

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

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

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

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

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

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

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

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

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

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

вот именно!

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

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

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

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

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

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

но... если вы не хотите связываться с 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:

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

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

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

решение

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

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>) и добавил a .триггер события для события 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 hover method ведут себя по-разному (только в эффекте наведения). Кнопка 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);
        }
    });

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

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

  $('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...
}

но вы должны проверить для ежевики, дроидов, набор других сенсорных устройств. Вы также можете привязать курсор мыши только в том случае, если агент пользователя содержит 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" />