Why your click events don't work on Mobile Safari
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.
/* This site is not particularly responsive, but since there's doubtless going to be people visiting this page on a mobile Apple iCrap device, let's at least make this post play nice with such mobile devices */ #img-iphone-click { float: right; height: 551px; margin-bottom: 1em; margin-left: 2em; width: 288px; } @media(max-width: 1023px) { #img-iphone-click { height: 276px; width: 144px; } } @media(max-width: 640px) { #img-iphone-click { height: 184px; width: 96px; } } 
#1 Adjacent input type=search
This just has to be a bug. It occurs under very specific conditions:
- You have a
:hoverrule 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
:hoverrule. In my testcase, this would be by removing thea:hover { }rule. - Remove the adjacent sibling rule, or make sure it doesn't match (kinda pointless). In my testcase, changing
div + \* { }todiv + 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. - 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
onclickhandler. - The
cursorproperty set topointerin 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.