5
votes

So, quite recently I've been working on a website that was given me to improve and make responsive, and one of the issues that I've been faced with is that there are many elements that are clickable, with a mixture of CSS and jQuery effects for hover states.

Now, firstly I'd prefer all of these hover states to be CSS, but the main issue I'm having is that on these hover states, certain elements are changing display and visibility css properties. I did some reading, and apparently if this is the case, on touchscreen iOS devices, this causes the first 'touch' to force the hover state, and then a second click is needed to actually click the element.

I'm trying to find a solution that doesn't require lots of markup and styling changes. Preferably a fix harnessing jQuery/JavaScript would be good.

I've tried the following:

$(document).ready(function() {
   $('a').on('click touchend', function(e) {
      var el = $(this);
      var link = el.attr('href');
      window.location = link;
   });
});

However, this has issues when the user holds their finger down on a clickable element, and drags the page to scroll. When they release their finger after dragging, the window.location is still changed.

I'll add a jsFiddle later if necessary.

Thanks in advance.

EDIT:

Here's a jsFiddle that shows the issue. http://jsfiddle.net/0bj3uxap/4/ If you tap one of the blocks, you'll see it shows the hover state, you then need to tap again to actually fire the click event.

It seems to happen when an element is hidden, and then the hover state shows the element.

3

3 Answers

4
votes

Looks like I found a solution.

https://github.com/ftlabs/fastclick

FastClick fixes this issue, and removes the 300ms delay issue with some mobile browsers.

Just include the library in <script> tags, and then initiate it using jQuery, or whatever you prefer:

$(document).ready(function() {
    FastClick.attach(document.body);
});

Just to explain briefly why the issue occurs:

When an element is hidden, for example when it has a CSS property of any of the following:

display: none;
opacity: 0;
visibility: hidden;

And the hover state of the hidden element then shows the element, iOS doesn't fire a click event on the first touch, it forces the hover state (to show the element). The user then needs to touch the element again for a click event to fire.

I see why this has been added, but I think I'd rather iOS didn't do this, and then developers would just need to tailor their websites to not hide content that coud be vital.

1
votes

If it helps anyone else: In my case I had a very similar problem, however it wasn't simply due to a :hover style on it's own. Instead, it was due to the fact that I was using JavaScript event listeners (touchstart, touchmove and touchend) to change visibility of elements on the page (no matter where).

In my case, I was simply adding a touch class to the <html> tag in order to detect that the device was capable of touch and should always display certain elements that typically only show on hover. My fix was two fold:

  1. Move to a >300ms delay (i.e. the amount of time mobile browsers may typically wait before determining if this was a single vs. double click). In my case, I just settled on 500ms (see #2 below for why).
  2. I then used a cookie to temporarily retain this setting so that these elements would be visible immediately and no touch event listeners would be required on subsequent page loads (thus a delay of 500ms on the first occasion shouldn't be a deal breaker).

Example code:

In this case, using jQuery + https://github.com/carhartl/jquery-cookie (modified to support maxAge).

function initTouchSupport() {
    // See if touch was already detected and, if so, immediately toggle the touch classes.
    if ($.cookie('touch-device')) {
        toggleTouch();
        return;
    }

    // Be efficient and listen once and, if ever detected, flag capability and stop listening (for efficiency).
    var events = 'touchstart touchmove touchend';
    $body.on(events, detectTouch);
    function detectTouch() {
        // Detected; retain for a short while (e.g. in case this is a laptop with touch capability and they switch
        // to mouse control). That way there's no delay on the next several page loads and no chance of a double-touch bug.
        $body.off(events, detectTouch);
        $.cookie('touch-device', true, {
            path: '/',
            domain: getDomain(),
            maxAge: 86400 // 86400 seconds = 1 day
        });

        setTimeout(toggleTouch, 500);
    }

    function toggleTouch() {
        // Swap out classes now
        $html.toggleClass('no-touch', false);
        $html.toggleClass('touch', true);
    }
}
0
votes

I had a very similar issue in IOS having to double tab buttons etc I removed all the desktop styles which included some hover styles this made no difference. I put the hover styles back in which are not used in the mobile UI. In then end the issue was a css class called

.error-message

Correction it turns out this css has been used in our UI and it was linked to a mouseover event