Статьи

Задержка клика 300 мс и iOS 8

ios_delay_header

Ох, задержка клика 300 мс. Это моя вторая любимая проблема тайной веб-разработки — сразу после определения того, какой элемент находится в фокусе в iframe .

Если вы не знаете, что такое задержка клика 300 мс, вы можете начать с чтения этого руководства, которое я написал для него в прошлом году , но вот версия tl; dr : в мобильных браузерах есть жест, известный как двойное касание для увеличения и двойного нажатия. нажмите, чтобы увеличить масштаб, браузеры не запускают clickсобытие (или различные события мыши) в течение ~ 300 миллисекунд после того, как пользователь коснется экрана. Проще говоря, без задержки браузеры не могут различить «клик» и первую половину двойного нажатия.

Это поведение легко проверить с помощью следующего фрагмента кода, который измеряет время между touchendсобытием (которое не задерживается) и clickсобытием (которое есть):

<button>Click</button>
<p>Delay was <span></span> milliseconds.</p>
<script>
    var button = document.querySelector( "button" ),
        span = document.querySelector( "span" ),
        delay;

    button.addEventListener( "touchend", function() {
        delay = Date.now();
    });
    button.addEventListener( "click", function() {
        span.innerHTML = Date.now() - delay;
    });
</script>

GIF ниже показывает поведение в действии на iPhone под управлением iOS 7 (мы кратко перейдем к iOS 8). Обратите внимание, что задержка между нажатиями кнопок и clickсобытием составляет чуть более 300 миллисекунд.

КСН-задержка

Вы можете самостоятельно проверить это поведение, запустив http://jsbin.com/xiculayadu на своем мобильном устройстве.

Так в чем проблема? Как оказалось, люди могут обнаружить 300 миллисекунд, а приложения с задержкой чувствуют себя вяло. Фактически, исследования показали, что «0,1 секунды — это предел того, что пользователь чувствует, что система реагирует мгновенно» . Таким образом, кратко, мобильные приложения, которые не обращаются к задержке щелчка, чувствуют себя медленными.

Что делается

Хорошей новостью является то, что все производители мобильных браузеров признали эту проблему. Плохая новость в том, что они выбрали три разных способа справиться с этим.

Chrome и Firefox

Chrome и Firefox для Android отключают задержку при использовании <meta name="viewport">тега, который устанавливает ширину области просмотра равной ширине устройства (или меньше), например <meta name="viewport" content="width=device-width">.

Использование <meta name="viewport" content="width=device-width">является одним из основных элементов адаптивных проектов, поэтому большинство адаптивных приложений уже исключены из задержки щелчков в Chrome и Firefox.

Вот тот же пример синхронизации, который выполняется в Chrome для Android с добавлением тега meta viewport. Обратите внимание, что теперь между touchstartи clickсобытиями практически нет задержки :

Android задержки

Internet Explorer

Internet Explorer позволяет избежать задержки с touch-actionпомощью свойства CSS . Установив touch-actionсвойство для всех интерактивных элементов в noneили manipulation, вы можете легко устранить задержку щелчка в своих приложениях.

Вот наш пример синхронизации, работающий в IE с добавлением <style>a[href], button { touch-action: manipulation; }</style>для удаления задержки клика. Обратите внимание, что, как и в Chrome, задержка теперь по существу прошла.

окна-телефон задержка

Несмотря на различие, решения Chrome / Firefox и IE являются разумными и действительно простыми в реализации. Через несколько минут вы можете убрать задержку в 300 мс в трех из четырех крупных мобильных браузеров. Теперь, когда мы рассмотрели правильные решения, давайте перейдем к iOS.

Обновление (7 января 2014 г.) : я по ошибке опущен, что Chrome также поддерживает touch-actionначиная с v36 , а Firefox touch-actionвнедряется за флагом с v29. Я называю touch-actionэто решением IE, потому что они были первыми, кто его реализовал, и у них пока нет решения на основе области просмотра ( хотя это может скоро измениться ).

Любопытный случай iOS

Исторически не было встроенного способа избежать задержки на iOS. Разработчики предложили решения Chrome и IE для отслеживания ошибок WebKit (см. Тикеты № 122212 и № 133112 , соответственно), но ни на одном из них не действовали.

Затем вышла iOS 8. Поскольку iOS Safari тайно разрабатывается в подземном бункере, сообщество веб-разработчиков было вынуждено перепроектировать браузер, чтобы узнать, были ли внесены изменения в задержку. Патрик Х. Лауке , который проводит сумасшедшую серию тестов событий касания и указателя , обнаружил, что что- то изменилось, и он, вместе с сообществом веб-разработчиков , в конечном итоге свел его к серии эвристических исследований. Да, эвристика — возможно, вы захотите выпить кофе.

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

Whether a tap is fast or slow is determined by how long your finger is on the screen, or more specifically, the difference between the touchstart and touchend events. Taps where your finger is on the screen for < ~125 milliseconds are considered fast, and the delay is maintained; taps where your finger is on the screen for > ~125 milliseconds are considered slow, and the delay is removed.

To show this in action, let’s return to the timing example. In the video below I tap the screen twice. The first tap is a slow tap, and the delay before the click event is quite small; the second tap is a fast tap, and the delay is still present.

Why would Apple do this? Fast taps are far more likely to be the first half of a double tap to zoom, so after a fast tap, Safari maintains the delay to wait for a potential second tap. A slow tap is unlikely to be part of a double tap to zoom, so Safari immediately fires the click without a delay.

I’ll let you decide whether this approach is clever or insane, but it’s important to know that these heuristics exist, and that the click delay no longer exists for slow taps in iOS 8 Safari.

Got it? Good, because we’re not done yet.

The UIWebView caveat

Just in case you weren’t confused enough, there’s one more caveat to add: the new iOS 8 heuristics are only present in iOS 8 Safari and iOS’s new WKWebView class. The heuristics are not present in the legacy UIWebView class, and they’re also not present for apps running as home screen web apps.

So does that mean your app’s behavior can vary depending on how it’s opened on iOS? Yes, yes it can. Although home screen web apps aren’t too common on iOS, UIWebView is still used a ton. The Facebook app uses it; the Twitter app uses it; heck, even Chrome for iOS is using a UIWebView under the hood.

What does this mean for my apps?

In my previous article I recommended using a library such as Kendo UI Mobile, FastClick, or Tappy! to workaround the problem for you. And if you’re using one of these libraries already you shouldn’t have to change a thing.

These libraries take a creative approach to avoid the delay altogether: they listen for touchend events instead of click events. When they detect a touchend event, they immediately create a synthetic click event using document.createEvent. Next, to avoid triggering click handlers multiple times, the libraries suppress the “real” click event.

By using a fast click library you ensure your click event handlers fire immediately without you having to worry about the plumbing to make that happen yourself. This approach has proven to work quite well, and it even functions fine in the context of the new iOS 8 heuristics. Therefore if you’re already using a fast click library of some variety you shouldn’t have to worry.

Do you need a library at all?

The more interesting question is whether you need a fast click library at all now that there are fast click solutions (of some variety) in all mobile browsers. After all, although fast click solutions work, there is a performance and maintenance cost associated with including a JavaScript library. Like all programming decisions the answer depends on your circumstances, but I would say for most web apps the answer is still yes.

Most web apps still receive a lot of views from mobile browsers without a baked in fast click solution, including the old Android browser, Chrome versions < 32, iOS 7, UIWebViews, and iOS home screen web apps. Those numbers are going to go down over time as users and developers upgrade, but at the moment, if you don’t include a fast click JavaScript library you’re leaving a lot of users with a suboptimal experience.

iOS 8 also complicates things. Even though it has a fast click “solution”, that solution is only present under certain circumstances, and using a fast click JavaScript library makes your app feel responsive regardless of whether your users are fast clickers or slow clickers.

That being said, it is unfortunate that we still need to include a JavaScript library to workaround a limitation of the mobile web. Hopefully Apple will follow in Internet Explorer or Chrome’s footsteps and implement something sane in iOS 9, but I’m not holding my breath.

Header image courtesy of Evil Erin