micadelli
micadelli

Reputation: 2482

event.preventDefault() not canceling link direction in jQuery Mobile

Background
I've been struggling with this stupid little problem of trying to cancel link direction when click event is fired on an anchor element in jQuery Mobile app.

Let's say I have a simple multi-page document like this:

<div data-role="page" id="page-1">          
    <div data-role="content">
        <a href="#page-2" id="mLink">page 2</a>
    </div><!-- /content -->
</div><!-- /page -->

<div data-role="page" id="page-2">
    <div data-role="content">
    </div><!-- /content -->
</div><!-- /page -->

... and JavaScript like this:

(function (MyApp, $, undefined) {
    'use strict';

    // Initializes app
    function init() {       
        $('#mLink').on('click', function (event) {
            event.preventDefault();
            //event.stopPropagation();
            //event.stopImmediatePropagation();

        });
    }

    // jQuery Mobile is ready now -> override defaults
    $(document).on("mobileinit", function () {
        // Set the default page transition
        $.mobile.defaultPageTransition  = 'slide';
    });

    // jQuery Mobile is ready now 
    $(document).ready(init);

}(window.MyApp = window.MyApp || {}, jQuery));

Problem
I just can't figure out why event.preventDefault() isn't canceling the page transition. However, if I add event.stopPropagation() it will cancel it. Also if I remove the jQuery Mobile library from the app, it works without event.stopPropagation().

Question
Is this normal behavior and is it ok to always call them both in the handler to cancel the link direction?

Notes
I'm using jQuery Mobile only for it's multipage template, navigation and transition capabilities.

Additional question
The question lurking behind my original question kind of was 'what jQuery Mobile does to a link that intercepts event.preventDefault() method to not prevent the link's default action, i.e. going to the target page.?'

Upvotes: 2

Views: 14118

Answers (2)

Elias Van Ootegem
Elias Van Ootegem

Reputation: 76395

I must admit that I'm not too familiar with jQuery mobile, but I'm not at all surprised by this. I've recently written a few script for mobile devices (pure JS) and the first thing you notice is the complete absence of a click event on most of them. Everything is touch now.

The thing is, touch events are heavily overloaded: when the user touches the screen and holds for X time, he might be trying to select some text, of get a magnifier, or that initial touch might be the start of a swipe. There isn't really a default action to be taken on touchstart. Most scripts that offer you a tab event, just add a listener for a touchstart event that binds a touchend listener to the touched element for Xms, and map that to a click.

Prevent default allows doesn't stop the event as such, its default action (if any) doesn't fall through, but the event propagates through the dom all the same, the touchend listener is set and that handler will be the one that kicks everything off. If you were not to prevent the default action, but just stop the event from propagating further down the DOM, towards its target, my guess is the touchout handler never gets bound, or at least never gets fired. Put simply: as far as the element is concerned, the touchstart event never reached it, so it never occurred if a touchend is fired without a touchstart, that must mean the touch started elsewhere in the DOM.

That, I think, explains why you get this slightly unexpected behaviour. Now your questions:

Without the jQuery mobile, the click handler is called. I know I just said there is no real click event on touch devices, but links do fire click handlers that are directly bound to the elements. However, the click event can't really be delegated on mobile devices, only direct binding seems to do the trick. Perhaps you might be able to catch them in their bubbling phase, but for anchors, that does limit your options a bit too much. In your snippet, your binding a click handler directly to your element, so that might just explain why it works with regular jQ, and not the jQm.

Normal behaviour? Yes, I do believe so. In my experience it is. Is it ok to call both methods to stop the event? Of course, as long as you know what the difference between the two is, and you know when and why you would call only 1. But if you want to kill the event, I see no harm in calling both.

Note: Both answers already given say that return false; amounts to the same as calling both stopPropagation and preventDefault. While that is true in jQuery, that is NOT the case when you find yourself writing pure JS. In pure JavaScript returning false from your handler is the same as only calling preventDefault, so be careful.
For once MS' JScript implementation is a good example of this:

//w3c:
e.preventDefault();
e.stopPropagation();
//IE:
e.returnValue = false;
e.cancelBubble = true;

the handler returns false === the event object has a return value of false, but its ability to bubble is left unchanged.

Upvotes: 3

Jonathan Wilson
Jonathan Wilson

Reputation: 4295

Normal? Yes. OK to call both? Yes.
event.preventDefault() is the only tool you have to prevent the browser's default behavior and event.stopPropagation() is the only tool you have to prevent competing event handlers from firing.

If you want a one-liner, you can use return false to achieve the same result. From http://api.jquery.com/on/:

Returning false from an event handler will automatically call event.stopPropagation() and event.preventDefault(). A false value can also be passed for the handler as a shorthand for function(){ return false; }. So, $("a.disabled").on("click", false); attaches an event handler to all links with class "disabled" that prevents them from being followed when they are clicked and also stops the event from bubbling.

Upvotes: 6

Related Questions