jquery preventing hover function on touch
Asked Answered
D

5

11

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"
    });
});
Dronski answered 17/1, 2013 at 21:53 Comment(4)
Try event.preventDefault() with hover. api.jquery.com/event.preventDefaultEquivoque
hmm how would you write that into it?Dronski
@Mihir That doesn't stop the method from executing, nor does it prevent the specified handlers from running. It prevents the default action, like the name suggests, which isn't the problem as described.Shrievalty
Ok, sorry I misunderstood. Can you not detect if the device is a touch device? If it is then don't call the function.Equivoque
D
-5

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"
        });
    });
}
Dronski answered 17/1, 2013 at 22:22 Comment(1)
It's not touch detection. Is detection "if user is fan of apple". Why this is accepted answer?Mechling
L
15

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.

Lulalulea answered 17/1, 2013 at 22:4 Comment(4)
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. +1Shrievalty
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.Dronski
@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.Shrievalty
ah damn i kind of suspected that but I had already changed it and didnt want to make it again haha ... code remoddedLulalulea
O
5

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.

Overlay answered 17/1, 2013 at 22:18 Comment(2)
this will totally work, but adds completely unnecessary overhead of bringing in a library.Lulalulea
... 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 :)Overlay
K
3

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
    });
Kentiga answered 10/5, 2016 at 6:0 Comment(2)
This was exactly what I needed. Thanks.Tripod
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.Grobe
E
2

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.

Epley answered 22/11, 2013 at 20:47 Comment(0)
D
-5

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"
        });
    });
}
Dronski answered 17/1, 2013 at 22:22 Comment(1)
It's not touch detection. Is detection "if user is fan of apple". Why this is accepted answer?Mechling

© 2022 - 2024 — McMap. All rights reserved.