Stuck on :hover

Whether or not this is good UX is a discussion for another time.

Posted May 28 2011
CSS, JavaScript, Mobile, Web
Tweet this article

It took me a long time to figure this out. More tal­ented devel­opers might have had an easier time of it. This is for the mor­tals among us who struggle. I hope this saves someone a little time.

Touch­screen events are, from what I gather looking at blogs and forums, not entirely well under­stood. In addi­tion to unique touch events, browsers for smart­phones and tablets also fire ‘legacy’ events that are defined for the desktop-and-mouse user: mouse­down, mouseup, hover. These appear to occur after the touch events.

When tap­ping on a link, you can see the pro­gres­sion of events. First is the touch­start. Then the touchend. The link is high­lighted by the browser. Then, strangely, the hover event occurs. Finally, the link is followed.

I don’t mind this behavior. By spec­i­fying a cer­tain hover state for a link, I can have that link ‘light up’ when touched before the browser moves along to the URL in the HREF.

The problem occurs when going back to the page with the orig­inal link. The hover state is sticky. The link remains ‘lit up’, even without any inter­ac­tion. Appar­ently, the most recently clicked link retains the browser focus. This is sim­ilar to the behavior of links in Internet Explorer where the most recently clicked is out­lined (which can be mod­i­fied using CSS). Unlike in IE, the focus is not lost until the user clicks another link. At that point, the sticky hover state adheres to the newly tapped link.

Whether or not this is good UX is a dis­cus­sion for another time. For a designer, it is damned annoying. If there were a sep­a­rate state for the most recently active link, or if we could simply use the :vis­ited link state, that would be fine. CSS could be used to inde­pen­dently style that state in a way con­sis­tent with the site design. But there doesn’t appear to be.

Man­u­ally removing the focus is not easy. You can’t do it when the page is loaded because the onload event doesn’t fire when using the back button. You can’t remove the focus on the click event because the hover event seems to occur after the click event on in the touch envi­ron­ment (or at least it seems to on the iPhone). Employing the jQuery hover() han­dler instead of the CSS :hover selector will not make any difference.

After much trial and mostly error, I set­tled on this solution:

<body onunload="$('a').blur();">

This is actu­ally the first I’ve known about the onun­load event han­dler. For the unini­ti­ated, the unload event occurs right before the browser leaves the page. By blur­ring all of the links in the page on unload, I was able to un-stick the hover state. Returning to the orig­inal page using the back button shows the clicked link in the default state.

I under­stand why browsers do what they do. The same ratio­nale under­lies the :vis­ited state of a link — a clue for the user as to which link was selected. How­ever, there should be some method avail­able for designers and devel­opers to cus­tomize this behavior to meet the design and style of each website.