2013-06-07: Why your click events don't work on Mobile Safari

Posted at 2013-06-07 00:33:10 by SHD

After ensuring that your newly created website works great on all desktop browsers, you put in the effort to make the site responsive. Everything is spot on with any of the Android browsers, but then you test on iOS/Mobile Safari and some of your click events aren't registering. Why is that? In my most recent project, I've encountered two different causes for this. Oddly enough, neither has anything to do with JavaScript, but CSS is the culprit. While one of them might be by design, the other is most definitely a bug, in my opinion. Since Mobile Safari is hell to debug (especially since in iOS 6, the developer console was removed and the only way to debug is by using desktop Safari, which isn't even available for Windows any more — probably a good thing, as the Windows versions were utter crap), it took ages to find the cause of this and I figured I'd spare you the trouble. Here's what happens and how to fix it.

#1 Adjacent input type=search

This just has to be a bug. It occurs under very specific conditions:

  • You have a :hover rule for an element that is clickable. The styles in that rule don't matter, the rule can even be empty.
  • and you have a CSS3 adjacent sibling rule with a most specific term that matches at least one element in the DOM. It doesn't need to match the entire rule. Again, the rule can even be empty.
  • and the clickable element is followed by an <input type="search"> element (which could also be wrapped as the first element inside a <form> element).

In that case, tapping the clickable element (whether that click is a JavaScript onclick handler, or just a simple <a href="...">) does not register. Instead, it applies the :hover style and only on the second tap does it navigate or run the JavaScript. Here's a minimal testcase. As far as I've seen, this happens only on Mobile Safari, so try it with an iPhone or somesuch. Changing pretty much anything will make the bug disappear, so all of these are valid workarounds:

  • Make sure the clickable element does not match any :hover rule. In my testcase, this would be by removing the a:hover { } rule.
  • Remove the adjacent sibling rule, or make sure it doesn't match (kinda pointless). In my testcase, changing div + * { } to div + h1 { } is sufficient to prevent the bug because there is no <h1> in the DOM. div + p { } would still trigger the bug, because there is a <p> element in the DOM. It doesn't seem to matter that there is no <div> in sight.
  • Insert another element in between, such as an empty <span></span>.
  • Probably the most feasible: change the <input type="search"> to an <input type="text"> instead.

#2 No event bubbling without a cursor style

This one may be as intended, but I still think it's a nasty one. For my project, I dynamically needed to add and remove certain elements that were not anchors, but were clickable. It would be inefficient to bind an onclick handler to each and every one of those elements, so instead, I used a handler on their container element and simply did a check in that. It is a popular construct in jQuery, too:

$('body').on('click', 'div.clickable', function(){ ... });

By virtue of event bubbling, the parent (<body> in the jQuery example) catches all child elements (<div class="clickable">) being clicked. Such a construct enables you have only 1 click handler for any number of matching elements without needing to bind them to each element individually. This works on every standards-compliant browser. Mobile Safari chooses to differ, however. It does not generate click events for elements that do not have either or both of:

  • A directly bound onclick handler.
  • The cursor property set to pointer in CSS.

Again, I've created a minimal testcase. Every browser I've encountered, other than Mobile Safari, will properly register clicks on both "buttons".

As an obvious workaround, either bind the onclick handler to each individual element or add a CSS rule: .clickable { cursor: pointer; }. This may be by design as a performance optimisation, but I do think it's rather bad form for behaviour mandated by the standard to be affected by purely presentational styles. The cursor isn't even visible on a touchscreen device!

Note: version used for testing was Mobile/10B329 Safari/8536.25, iOS version 6.1.3 on an iPod Touch, but I've seen it happen on iPhones too.

Pingbacks

Comments

I know this post is hardly recent, but I just wanted to thank you. After two hours of hunting for a solution, yours was the first post I found that explained what was going wrong.

The lack of event bubbling without a cursor style completely threw me - I couldn't work out where I had gone wrong as walking through my code was shedding no light on the issue. Mobile safari and chrome on iOS were both showing this behaviour. I believe it's because the browsers use the onClick attribute or the cursor property to decide whether non-natively clickable elements should generate a click event, but the inconsistency with desktop browsers caught me out.

Once again, thank you.

Posted at 2014-02-22 15:02:36 by Ben

Thanks a lot dearr... woRKED

Posted at 2014-03-19 13:58:54 by Mirza V U

I'm really glad you posted this. Props for sharing your findings!

Posted at 2014-05-14 15:15:43 by Mark Churchill

Thank you so mutch for this post. You solved my problem.

Posted at 2014-06-29 05:02:51 by Andre

Thanks a lot man. Woooo hoooo !!! Thank you, thank you, thank you, thank you, thank you!

Posted at 2014-11-24 18:55:18 by filip

You're God!. Thank you. I was pulling my hair with mobile slide out navigation menu working erratically. I removed :hover state from links and it fixed it. Thank YOU!

Posted at 2014-12-02 20:24:04 by edward

Yes. Totally saved by this post. Thanks.

Posted at 2015-01-20 20:12:11 by Ben Hirsch

Thank you so much! You saved me many hours for figuring out why body on click not working on ISO.

Posted at 2015-05-11 10:23:02 by Sky

Thanks for your thorough post. Tested on iOS 6.1 Hardware 10B141.

Posted at 2015-05-14 16:47:10 by Alex

Still relevant as of today in Chrome and Safari on an iPhone 6.

Posted at 2015-07-13 19:06:32 by Jon Welker

I really really reall appreciate this article, it helped me found the solution of the problem, I've been looking for what was going wrong on safari from my mobile phone and couldn't find a solution, thank you so much for this. much appreciated. +1

Posted at 2015-08-19 22:57:20 by Gabriele Tiboni

This was super helpful, #2 still happening on at least iPad Mini 2s and iOS 8.3

Posted at 2015-10-13 15:15:37 by Kyle

Folks -

Sort of off-topic, but I figured I'd give it a shot....

Do you have any idea why my basic input button (input type=email, etc) works in literally every browser, but is completely useless in Safari? I've tested with Yosemite and El Capitan....nada. Is there any logic to be had here, a command to run, a test to do, some bug that everyone knows about but me?

Would be most grateful for any thoughts -

Chris

Posted at 2016-01-26 04:42:01 by Chris

Had this issue in both mobile and Chrome and Safari.

For me, it happened under different circumstances, and unfortunately, the fixes above did not work in my case.

What did work was that I just turned the clickable element into a button element. Not sure this would work in all use cases, but might be helpful for some people.

Posted at 2016-02-26 19:09:21 by Chris Dutrow

You are a genius! Thank you! Adding the cursor: pointer; to my class fixed it, along with making sure the element had a width. It fixed it for both Chrome and Safari on iPhone 6s Plus (iOS 9.2) and iPhone 6 Plus (9.3.1).

Posted at 2016-04-23 01:51:17 by Carry

Wow! I would've never come up with this solution. Thanks for posting this!

Posted at 2016-05-01 05:40:58 by David

Late to this but THANK YOU!

Posted at 2016-05-10 04:45:09 by P

I still can't get mobile Safari to respect my click events : (

I'm using a play-pause button, mute button, fullscreen and such for custom html5 video controls. Safari doesn't respect anything. Just the play button. And when I swap out the image of the pause for play, it shrinks my play button down reeeeeal tiny so it looks cute and harmless.

Makes me wanna punch an apple.

Posted at 2016-07-03 04:08:07 by Costa

Thanks Steven! You saved me quite some time.

Posted at 2016-09-08 15:05:58 by Michal

On some sites I need to use css "cursor:help". Only iOS it gave me a lot irritation... To solve this, change everything af the page to "cursor:pointer". That works for me.

jQuery:
if (/iP(hone|od|ad)/.test(navigator.platform)) {
$("*").css({"cursor":"pointer"});
}

Posted at 2016-10-03 09:27:51 by Wobbo

Oh my god, adding that cursor: pointer solved it!

Imagine. The solution is to add a cursor icon CSS rule, FOR A MOBILE BROWSER. Gah so annoyed right now, I could drop-kick a llama.

Thank you for this!

Posted at 2016-10-16 08:18:15 by Ash Menon

I've never read of such a ridiculous set of circumstances causing a bug like this. I applied the advice from this post, and sure enough my problems were resolved. This one goes down in the history books.

Posted at 2016-12-07 23:28:28 by Dane MacMillan

Another way to get click´s to work on Safari is not to use <div> as a click target. Just use a an <a> for example. "Use a typically interactive element (e.g. <a>) instead of one that isn't typically interactive (e.g. <div>)." From MDN

Posted at 2016-12-19 17:19:23 by Sebastian

Great post & great comments. I thought I'd add this, since nothing here quite worked for me.

function overlayClickHandler() {
console.log('Do stuff...');
}

["click", "touchstart"].forEach(function(event_type) {
document.body.addEventListener(event_type, overlayClickHandler, false);
});

Posted at 2017-01-07 11:25:53 by Andy

That's incredible! Cursor pointer fixes the click bug...

Posted at 2017-02-09 15:44:57 by Slakjejasper

This solution did not help me unfortunately, but I was able to figure out a solution. I had a toggle menu that didn't work well when it was clicked on in the mobile safari. The JavaScript was : window.onclick = function(event) { ......
once I changed it to window.HTMLButtonElement = function(event) {....
It worked fine.

Posted at 2017-03-27 21:42:37 by Josh

Thanks it works
it save my 12 hours :)

Posted at 2017-03-30 12:40:11 by Salakin

Post a comment

Note: HTML is not permitted, URLs will be linked automatically. Spam comments will result in a permanent ban.
Type these 4 symbols into the edit field