How Mobile Safari emulates mouse events
— web — 2 min read
When you are adapting web apps to touchscreen devices particular
challenges come up around events like
Touchscreen devices like the iPad do not have a cursor, so the user
cannot exactly move the mouse over an HTML element. However, Mobile
Safari, the web browser that comes with the iPhone and iPad, has
a fallback for websites that require hovering or cursor movement.
Usually when you tap on an element on a link or other clickable element
Mobile Safari translates that into a regular
click event. The browser
also produces some touch events that do not exist in a lot of browsers.
But from the perspective of a web page that was not designed with
a touchscreen in mind, what you get is a plain
specifically, the browser fires
that order. But if a clickable element also does something on
mouseover then tapping on that element will trigger a
instead of a
click. Tapping on the same element again will produce
click event. A random example of a page that exhibits this behavior
is the schedule page from the Open Source Bridge
website. Try tapping on session titles and see what happens.
Mobile Safari will only produce mouse events when the user taps on
a clickable element, like a link. You can make an element clickable by
adding an onClick event handler to it, even if that handler does
nothing. On tap Mobile Safari fires the events
click in that order - with
some caveats which are explained below. Those events all fire together
after the user lifts her finger. You might expect the
to fire as soon as the user presses her finger to the screen - but it
does not. When the user taps on another clickable element the browser
mouseout event on the first element in addition to firing the
aforementioned events on the new element.
So how do we get to the behavior where one tap emulates
a second tap emulates
click? It turns out that after any
event handlers run Safari checks the DOM for changes and if the content
has changed it skips the
click events. So
these events do not fire. When the user taps on the same element again
mouseover event does not fire again, so the browser goes ahead
with the other events.
mousemove event behaves in a similar way: if the DOM has changed
mousemove handlers are finished running then Mobile Safari
skips the remaining events.
Safari does not accept just any change to a DOM element as a "content
change" though. Through testing I discovered that adding a regular
element to the DOM or showing a previously hidden element in
mouseover handler would prevent the
click event from firing. But
removing an element, hiding an element, or changing the content of
a text node do not prevent the
click event. I also tried adding class
names to elements - which Safari also did not treat as a "content
change". As far as I can tell only adding or showing an element will
click events to be skipped.
I created some fiddles on jsfiddle.net to test Mobile Safari
behavior. For your investigative pleasure I have
an example of a
mouseover handler that adds elements to the DOM,
another that shows a hidden element,
and a third that makes no changes to the DOM at all.