click
event, it’ll work on desktop screens but likely won’t do anything or behave wonky on a mobile screen. (e.g. the event listener not firing or having to tap two times for it to work)iOS Safari will only trigger click events for elements that
cursor
property set to pointer
onclick
attribute (can be empty, doesn’t matter)It’s a known browser bug for iOS Safari that has been around since 2010. It has been documented here, here, here and here. Here is a codepen demo of the issue.
Alternatively, you can listen to touch events instead of clicks. Preferably listen to both because we do have devices with both physical keyboards and touch screens. touchstart
, touchend
are two touch events. mouseover
and mouseout
are fallbacks for hovering and cursor movement.
iOS Safari translates taps to a regular click event. It fires mouseover
, mousemove
, mousedown
, mouseup
, and click
in that order. These events all fire together as soon as the user lifts the finger more on safari mouse events
You can listen to four kinds of touch events
touchstart
fired when user makes contact with touch surface and a touch point is createdtouchmove
fired when user moves a touch point along the touch surfacetouchend
fired when touch point is removed from surfacetouchcancel
fired when touch point has been disrupted (depends on the implementation, e.g. too many touch points created..)touchenter
and touchleave
were proposed drafts that haven’t been implemented
There is some browser specific stuff that can happen on touch events. For example:
The solution to prevent all these defaults (and using your custom action) is event.preventDefault()
The thing with handling multiple events like touchstart
and mouseup
is that they’ll fire twice in touch enabled browsers.
In jQuery, you can use the .on()
function that is capable of handling multiple events as well as event delegation
1$( "#dataTable tbody" ).on( "touchend click", "tr", function() {
2 alert( $(this).text() );
3});
.preventDefault()
.preventDefault()
can also be used to mark an event as handledfalse
prevents the event from bubbling upfalse
from an event handler will automatically call event.stopPropagation()
and event.preventDefault()
. A false
value can also be passed for the handler as a shorthand for function(){ return false; }
1$(this).on("touchstart click", function() {
2 // Do the magic
3 return false; // stop propagation, you've already handled it once
4});
If the listener has already responded to either one of the events, it’ll stop propagation.
In pure JavaScript, you can do something like below:
1<button onclick="startup()">Initialize</button>
1function startup() {
2 var el = document.getElementsByTagName("canvas")[0];
3 el.addEventListener("touchstart", handleStart, false);
4 el.addEventListener("touchend", handleEnd, false);
5 el.addEventListener("touchcancel", handleCancel, false);
6 el.addEventListener("touchmove", handleMove, false);
7 log("initialized.");
8}
onclick
attributes vs. event listenersWhile adding onlclick
attributes to elements may make them clickable on iOS Safari and be considered an easy fix, it is recommended that you keep your JS and HTML separate.
Instead of making elements clickable by adding cursor
values in CSS or onclick
attributes in HTML, you can do it all in JS only by adding an event listener that handles clicks as well as taps. Touch events work in mobile Safari regardless of whether the element is clickable or not.
1// using jQuery and .on()
2$('.button-group').on('mouseup touchstart', 'button.btn-wishlist', function (e) {
3 let product = $(this).attr('data-product')
4 wishlist.add(product);
5 e.preventDefault();
6});
Adding event listeners instead of using onclick
is the preferred way. If you have separate files for HTML and JS, your code should be separate too. Separation of concerns is recommended.
The added benefit of removing the onclick attributes from the buttons was the code becoming more DRY (Don’t Repeat Yourself). Imagine you have a table with 100 cells and you want to do something when any of the cells is clicked. Are you seriously going to add 100 onclick attributes? With a single event handler you can handle clicks on all those cells (because: event delegation).
Mobile browsers used to add a 300-350ms delay between touchend
and click
and waited to see if it was going to be a double-tap or not, since double-tap was a gesture to zoom into text. This has been removed. As of March 2016 and iOS 9.3, this delay is gone for mobile-optimised sites. What you do need to do is add a <meta>
tag to the site’s <header>
.
1<meta name="viewport" content="width=device-width">
This sets the viewport width to the same as the device, and is generally a best-practice for mobile-optimised sites. If you are using or have used Bootstrap, this meta tag is usually included in the getting started code.