:active pseudo-class doesn't work in mobile safari

<body ontouchstart="">
    ...
</body>

Applied just once, as opposed to every button element seemed to fix all buttons on the page. Alternatively you could use this small JS library called 'Fastclick'. It speed up click events on touch devices and takes care of this issue too.


As other answers have stated, iOS Safari doesn't trigger the :active pseudo-class unless a touch event is attached to the element, but so far this behaviour has been "magical". I came across this little blurb on the Safari Developer Library that explains it (emphasis mine):

You can also use the -webkit-tap-highlight-color CSS property in combination with setting a touch event to configure buttons to behave similar to the desktop. On iOS, mouse events are sent so quickly that the down or active state is never received. Therefore, the :active pseudo state is triggered only when there is a touch event set on the HTML element—for example, when ontouchstart is set on the element as follows:

<button class="action" ontouchstart=""
        style="-webkit-tap-highlight-color: rgba(0,0,0,0);">
    Testing Touch on iOS
</button>

Now when the button is tapped and held on iOS, the button changes to the specified color without the surrounding transparent gray color appearing.

In other words, setting an ontouchstart event (even if it's empty) is explicitly telling the browser to react to touch events.

In my opinion, this is flawed behaviour, and probably dates back to the time when the "mobile" web was basically nonexistent (take a look at those screenshots on the linked page to see what I mean), and everything was mouse oriented. It is interesting to note that other, newer mobile browsers, such as on Android, display `:active' pseudo-state on touch just fine, without any hacks like what is needed for iOS.

(Side-note: If you want to use your own custom styles on iOS, you can also disable the default grey translucent box that iOS uses in place of the :active pseudo-state by using the -webkit-tap-highlight-color CSS property, as explained in the same linked page above.)


After some experimentation, the expected solution of setting an ontouchstart event on the <body> element that all touch events then bubble to does not work fully. If the element is visible in the viewport when the page loads, then it works fine, but scrolling down and tapping an element that was out of the viewport does not trigger the :active pseudo-state like it should. So, instead of

<!DOCTYPE html>
<html><body ontouchstart></body></html>

attach the event to all elements instead of relying on the event bubbling up to the body (using jQuery):

$('body *').on('touchstart', function (){});

However, I am not aware of the performance implications of this, so beware.


EDIT: There is one serious flaw with this solution: even touching an element while scrolling the page will activate the :active pseudo state. The sensitivity is too strong. Android solves this by introducing a very small delay before the state is shown, which is cancelled if the page is scrolled. In light of this, I suggest using this only on select elements. In my case, I am developing a web-app for use out in the field which is basically a list of buttons to navigate pages and submit actions. Because the whole page is pretty much buttons in some cases, this won't work for me. You can, however, set the :hover pseudo-state to fill in for this instead. After disabling the default grey box, this works perfectly.


Add an event handler for ontouchstart in your <a> tag. This causes the CSS to magically work.

<a ontouchstart="">Click me</a>