57

This question is for the purposes of developing jQuery plugins and other self-contained JavaScript snippets that don't require modifying other script files for compatibility.

We all know that event.preventDefault() will prevent the default event so we can run a custom function. But what if we want to simply delay the default event before invoking it? I've seen various, case-specific ninja tricks and workarounds to re-invoke the default action, but like I said, my interest is in a universal way to re-trigger the default, and not deal with default triggers on a case-by-case basis.

$(submitButton).click(function (e) {

    e.preventDefault();

    // Do custom code here.

    e.invokeDefault(); // Imaginary... :(
});

Even for something as simple as form submission, there seems to be no universal answer. The $(selector).closest("form").submit() workaround assumes that the default action is a standard form submission, and not something wacky like a __doPostBack() function in ASP.NET. To the end of invoking ASP.NET callbacks, this is the closest I've come to a universal, set-it-and-forget-it solution:

$(submitButton).click(function (e) {

    e.preventDefault();

    // Do custom code here.

    var javascriptCommand = e.currentTarget.attributes.href.nodeValue;
    evalLinkJs(javascriptCommand);
});


function evalLinkJs(link) {
    // Eat it, Crockford. :)
    eval(link.replace(/^javascript:/g, ""));
}

I suppose I could start writing special cases to handle normal links with a window.location redirect, but then we're opening a whole new can of worms--piling on more and more cases for default event invocation creates more problems than solutions.

So how about it? Who has the magic bullet that I've been searching for?

Jake
  • 4,699
  • 2
  • 32
  • 44
  • 2
    I don't really get what you're trying to do... doesn't the event handler code run *before* the default action occurs (e.g. a `submit` event handler's code will run before the form is actually submitted... isn't that what you're trying to achieve?) – James Allardice Oct 11 '11 at 21:41
  • 2
    Is your custom code asynchronous? this might explain why you feel you need such a method, but even having that imaginary method won't solve your problem. – agradl Oct 11 '11 at 21:48
  • 1
    You definitely need to be more clear about what you want. If you want to run code within the handler, that code runs before the "default" behavior anyway. – Mike Edwards Oct 11 '11 at 21:56
  • `preventDefault` prevents the default action. If you want the default action to occur, don't prevent it. Seems pretty obvious. – Lightness Races in Orbit Oct 11 '11 at 22:00
  • 1
    maybe @Jake means something more like [this](http://stackoverflow.com/questions/18050538/invoke-the-default-action-of-a-specific-event-on-an-object) – Hashbrown Aug 05 '13 at 04:22

5 Answers5

13

Take a look at this one:

You could try

if(!event.mySecretVariableName) {
   event.preventDefault();
} else {
   return; // do nothing, let the event go
}

// your handling code goes here
event.originalEvent.mySecretVariableName = "i handled it";

if (document.createEvent) {
   this.dispatchEvent(event.originalEvent);
} else {
    this.fireEvent(event.originalEvent.eventType, event.originalEvent);
}

Using this answer: How to trigger event in JavaScript? and the jQuery event reference: http://api.jquery.com/category/events/event-object/

Tag the event object you receive so if you receive it again you don't loop.

Community
  • 1
  • 1
Mike Edwards
  • 3,712
  • 16
  • 22
13

Don't call preventDefault() in the first place. Then the default action will happen after your event handler.

Ben
  • 9,838
  • 5
  • 40
  • 42
  • 15
    This is the right answer as long as you're not needing to do the default handling asynchronously after your custom handling. – Jacob Oct 11 '11 at 22:10
  • No true in my `copy` handler, I need to return `true` for the text to be copied. Do nothing or return falsy values also don't work. But that's not the OP's question. – Hp93 Dec 02 '20 at 12:03
4

This should work. I've only tested in firefox though.

<html>
<head>
<script>
    window.addEventListener("click",handleClick,false);
    function handleClick(e){
        if (e.useDefault != true){
            alert("we're preventing");
            e.preventDefault();
            alert(e.screenX);
            //Firing the regular action
            var evt = document.createEvent("HTMLEvents");
            evt.initEvent(e.type,e.bubbles,e.cancelable);
            evt["useDefault"] = true;
            //Add other "e" attributes like screenX, pageX, etc...
            this.dispatchEvent(evt);
        }
        else{
            alert("we're not preventing");
        }
    }
</script>
</head>
<body>

</body>
</html>

Of course, you'd have to copy over all the old event variables attributes too. I just didn't code that part, but it should be easy enough.

Azmisov
  • 5,371
  • 5
  • 47
  • 66
  • 2
    +1 This is the correct approach. You should attempt to create the correct type of event - see https://developer.mozilla.org/en-US/docs/DOM/document.createEvent – chowey Mar 29 '13 at 04:32
3

It's not possible like JamWaffles has already proven. Simple explanation why it's impossible: if you re-trigger the default action your event listener intercept again and you have an infinite loop.

And this

click(function (e) {
    e.preventDefault();
    // Do custom code here.
    e.invokeDefault(); // Imaginary... :(
});

is the same like this (with your imaginary function).

click(function (e) {
    // Do custom code here.
});

It seems that you want to manipulate the url of your clicked element. If you do it like this it just works fine. Example.

styrr
  • 831
  • 4
  • 9
0

I needed to disable a button after click and then fire the default event, this is my solution

$(document).on('click', '.disabled-after-submit', function(event) {
    event.preventDefault();
    $(event.currentTarget).addClass('disabled');
    $(event.currentTarget).removeClass('disabled-after-submit');
    $(event.currentTarget).click();
    $(event.currentTarget).prop('disabled', true);
});
Tomáš Tibenský
  • 785
  • 10
  • 19