jquery preventing hover function on touch

13,244

Solution 1

ended up using touch detection:

var deviceAgent = navigator.userAgent.toLowerCase();
var agentID = deviceAgent.match(/(iphone|ipod|ipad)/);

if(agentID) { 
    $('#close').click(function() {
        var id = $(this).attr('id');
        $('#full_image').animate({
            height: 0
        }, 300, function() {
            $('#full_image img').attr('src','#');
        });
        $("#close_1").css({
            display: "none"
        });
        $("#close_2").css({
            display: "none"
        });
        $("#close").css({
            display: "none"
        });
    });
}
else {
    $('#close').hover(
        function() {
            $("#close_2").css({
                display: "none"
            });
            $("#close_1").css({
                display: "block"
            });
        }, function() {
            $("#close_1").css({
                display: "none"
            });
            $("#close_2").css({
                display: "block"
            });
        }
    ); 
    $('#close').click(function() {
        var id = $(this).attr('id');
        $('#full_image').animate({
            height: 0
        }, 300, function() {
            $('#full_image img').attr('src','#');
        });
        $("#close_1").css({
            display: "none"
        });
        $("#close_2").css({
            display: "none"
        });
        $("#close").css({
            display: "none"
        });
    });
}

Solution 2

Make the .hover() method more explicit and combine it with .on():

var $close1 = $('#close_1'),
    $close2 = $('#close_2');

$('#close').on({
    mouseenter: function(){
        $close2.css({display:'none'});
        $close1.css({display:'block'});
    },
    mouseleave: function(){
        $close1.css({display:'none'});
        $close2.css({display:'block'});
    }
});

Then combine that with .off().

$('#close').on('touchstart',function(){
    $(this).off('mouseenter,mouseleave');
});

If you want the event to fire on click with touch devices, but on hover on desktop devices, then put the functions as a separate function you call within those actions respectively.

EDIT

Been a while since I did this answer, here is a better way:

$(function(){
    var isTouchDevice = ('ontouchstart' in window || 'onmsgesturechange' in window),
        $close = $('#close'),
        $close1 = $('#close_1'),
        $close2 = $('#close_2');

    if(!isTouchDevice){
        $close.on({
            mouseenter: function(){
                $close2.hide();
                $close1.show();
            },
            mouseleave: function(){
                $close1.hide();
                $close2.show();
            }
        });
    }

    $close.on('click',function(){
        $('#full_image').animate({height:0},300,function(){
            $(this).find('img').attr('src','#');
        });

        $close.hide();
        $close1.hide();
        $close2.hide();
    });
});

This doesn't require a "hover prevention" event to fire with each touch, basically sets capabilities on page load while not affecting the click event.

Solution 3

I think a clear approach would be to:

  1. Detect if the browser supports touch events
  2. Add the hover event handler accordingly

If you're using something like Modernizr already:

if(!Modernizr.touch){
    // only if the browser doesn't support touch events,
    // add the hover handler here.
}
//add the click handler here, as you want it bound no matter what

See What's the best way to detect a 'touch screen' device using JavaScript? and What's the best way to detect a 'touch screen' device using JavaScript? for other options to detect touch capabilities.

Solution 4

On mobile side calling preventDefault in touchstart event prevents mouseover, mouseenter, mousedown and affiliated events. Detail: https://patrickhlauke.github.io/touch/tests/results/

    $('#close').on('touchstart',function(e){
        console.log('touchstart');
        e.preventDefault();
        //Do touch stuff
    });

Solution 5

Because of Windows 8 and Ultrabooks, I expect to see a lot of devices that support both touch and pointer events. As a result, I avoid disabling the hover event outright since it could potentially break the site for an touch enabled user with a mouse.

To solve this problem I ended up using two different classes for displaying menus, .hover and .touch, as well as separate events for hover and tap.

I'm using jquery.finger for capturing tap events, though any plug-in should work, this was just the smallest one.

The HTML Would be something like:

<li>
    <a>Some Link</a>
    <div>Some Content</div>
</li>

The CSS would be something like:

li div {display:none;}
li.hover div, li.touch div {display:block;}

And the Javascript using JQuery:

// Caching whatever elements I'm using for the navigation
a = $("a");
li = $("li");

// Set hover events
li.hover(

    // Both hover in and out fire whenever the user taps, aggravating!
    function(e) {
        // Close unused menus
        li.not(this).removeClass("hover").removeClass("touch");

        // Show this menu
        $(this).addClass( "hover" );
    }, function(e) {
        // Only closes if the menu doesn't have .touch, hell yeah!
        li.removeClass("hover");
    }

);

// Set the tap event
a.on('tap',function(e,data){
    e.stopPropagation();
    var thisParent = $(this.parentNode);

    // Close unused menus
    li.not(thisParent).removeClass("touch");

    // Toggle the current menu
    thisParent.toggleClass("touch");

    // The menu is open, so we don't need this class anymore
    li.removeClass("hover");
});

// Prevent the list items when being tapped from closing the drop down
li.on('tap',function(e){e.stopPropagation();});

// Close drop downs when tapping outside the menus
$(document).on('tap',function(e){
   li.removeClass("touch");
});

The important take away here is how I'm adding a seperate .hover or .touch class depending on the event, as well as removing the unused classes. The order is important so the menus don't blink.

Share:
13,244
loriensleafs
Author by

loriensleafs

Updated on June 13, 2022

Comments

  • loriensleafs
    loriensleafs almost 2 years

    I have a hover function, if it's a touch device I'd like the hover event to NOT happen. The problem is when you tap the link with a touch device it does the hover event before doing the click event, so you have to tap it twice for it to work.

    this is the hover function:

    $("#close").hover( 
        function () { 
            $("#close_2").css({
                display: "none"
            });
            $("#close_1").css({
                display: "block"
            });
        }, 
        function () {
            $("#close_1").css({
                display: "none"
            });
            $("#close_2").css({
                display: "block"
            });;
        }
    ); 
    

    and then I have this set up as the click function:

    $('#close').click(function() {
        var id = $(this).attr('id');
        $('#full_image').animate({
            height: 0
        }, 300, function() {
            $('#full_image img').attr('src','#');
        });
        $("#close_1").css({
            display: "none"
        });
        $("#close_2").css({
            display: "none"
        });
        $("#close").css({
            display: "none"
        });
    });
    
  • nbrooks
    nbrooks over 11 years
    Specifying 'mouseenter' and 'mouseleave' specifically shouldn't make a difference, since hover is simply shorthand for that; the second method seems like it should work though. +1
  • loriensleafs
    loriensleafs over 11 years
    hmm, so I added that little snipit in with the code I have posted above for hover and when I tap on the touch screen it still does the hover event first, then tap again and it does the click event.
  • nbrooks
    nbrooks over 11 years
    @lifeinthegrey Here is where you can actually try spelling out the methods, the shorthand hover only works as a method now, the pseudoevent's use is deprecated so it doesn't work in 1.8+. $('#close').off('mouseenter mouseleave');. Just an idea.
  • PlantTheIdea
    PlantTheIdea over 11 years
    ah damn i kind of suspected that but I had already changed it and didnt want to make it again haha ... code remodded
  • PlantTheIdea
    PlantTheIdea over 11 years
    this will totally work, but adds completely unnecessary overhead of bringing in a library.
  • freethejazz
    freethejazz over 11 years
    ... which is exactly why I said "If you're using something like Modernizr already" and included the links to SO questions discussing alternate methods of detection :)
  • Maciej Paprocki
    Maciej Paprocki over 9 years
    It's not touch detection. Is detection "if user is fan of apple". Why this is accepted answer?
  • Graham P Heath
    Graham P Heath almost 6 years
    This was exactly what I needed. Thanks.
  • Skeets
    Skeets over 4 years
    This answer actually works (most answers here don't consider devices that have touchscreens + a mouse, like a touchscreen laptop), and is also the simplest answer.