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

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

  • Добавьте эффекты наведения на 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 ссылку, эффект наведения также удаляется. Это гарантирует, что он будет удален, если пользователь нажимает кнопку "Назад" в веб-браузере.

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

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

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

Это!

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

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

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

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

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

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

Ответ 3

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

Ответ 4

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

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

Вот что говорит 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();
    }
});

Ответ 5

Решение cduruk было довольно эффективным, но вызвало проблемы на нескольких частях моего сайта. Поскольку я уже использовал jQuery для добавления класса hover CSS, самым простым решением было просто не добавлять класс hover 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");
        }
    );
}

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

Ответ 6

Не нужно делать сложнее.

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

Ответ 7

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

Ответ 8

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

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

Ответ 9

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

Проблемы, решаемые нами

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

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

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

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

Решение

В теории вы можете просто добавить флаг с touchmove, но iPhones запускают 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);

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

Ответ 10

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

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

Я создаю эффект с 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.

Ответ 11

Я только выяснил, что он работает, если вы добавляете пустой прослушиватель, не спрашивайте меня, почему, но я тестировал его на 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(){});
    }
}

Ответ 12

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

Однако мое решение было таким: В событии клика (или нажмите?) Вы установите таймер. Если ссылка снова нажата в течение X ms, вы просто вернете false.

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

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

Ответ 13

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

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

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, вы можете просто заменить функцию Modernizr.touch выше на ('ontouchstart' in document.documentElement)

Также обратите внимание, что тестирование пользовательского агента iemobile даст вам более широкий диапазон обнаруженных мобильных устройств Microsoft, чем Windows Phone.

Ответ 14

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

Приветствия,

Джероен

Ответ 15

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

Я решил это с помощью аналогичного метода, как описано выше, но я использовал плагин jQuery $.browser(для jQuery 1.9 > ) и добавил событие .trigger в событие привязки 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
});

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

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

Ответ 16

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

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

Ответ 17

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

Этот метод js предотвращает зависание на ipad и предотвращает регистрацию щелчка двумя щелчками в некоторых случаях. В CSS, если у вас есть: hover psudo classes в вашем css, измените их на .hover Например. Some-class: hover to.some-class.hover

Протестируйте этот код на 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 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

Ответ 18

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

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

Ответ 19

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

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

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

Ответ 20

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

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

Ответ 21

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

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

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

Ответ 22

Это работает для меня, когда у вас есть JQuery UI выпадающий

if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
      $('.ui-autocomplete').off('menufocus hover mouseover');
}

Ответ 23

Вы можете проверить navigator.userAgent следующим образом:

if(!navigator.userAgent.match(/iPhone/i) || !navigator.userAgent.match(/iPad/i)) {
    //bind your 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, вы можете получить большинство карманных компьютеров, но не все из них.

Ответ 24

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