14

I am using the jQuery trigger method to call an event... but it behaves inconsistently. Sometimes it call the event, sometimes it does not.

<a href="#" onclick="
    $(this).trigger('custom-event');
    window.location.href = 'url';
    return false;
">text</a>

The custom-event has lots of listeners added to it. It is as if the trigger method is not synchronous, allowing the window.location.href be changed before executing the events. And when window.location.href is changed a navigation occurs, interrupting everything.

How can I trigger events synchronously?

Using jQuery 1.8.1.

EDIT

I have found that the event, when called has a stack trace like this:

  1. jQuery.fx.tick (jquery-1.8.1.js:9021)
  2. tick (jquery-1.8.1.js:8499)
  3. jQuery.Callbacks.self.fireWith (jquery-1.8.1.js:1082)
  4. jQuery.Callbacks.fire (jquery-1.8.1.js:974)
  5. jQuery.speed.opt.complete (jquery-1.8.1.js:8991)
  6. $.customEvent (myfile.js:28)

This proves that jQuery trigger method is asynchronous. (I was wrong... this only proves that the event I was calling, had an animation inside it, and was calling the expected function inside the callback after the animation)

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Miguel Angelo
  • 23,796
  • 16
  • 59
  • 82

2 Answers2

28

You, my friend, are looking for jQuery "when".

http://api.jquery.com/jQuery.when/

To force anything to be synchronous, you can use something like this....

$.when($(this).trigger('custom-event')).done(function(){
    window.location.href = 'url';
});
Trent
  • 1,280
  • 11
  • 12
  • +1: Yeah! Thats it... thanks! jQuery `trigger` docs say nothing about this deferred behaviour... even if it is just a small delay, sometimes can cause big troubles. – Miguel Angelo Nov 12 '12 at 05:29
  • 7
    In fact I was wrong... `trigger` is sychronous... the problem was that there was an effect running inside the triggered event, and the expected method was being called inside the callback. – Miguel Angelo Nov 12 '12 at 05:37
  • 1
    The handler must return a Promise ;-) – Rodrigo Perez Burgues Nov 19 '13 at 11:40
  • 2
    Actually no. http://api.jquery.com/trigger/ shows that jQuery.trigger() returns the jQuery object in question, i.e. $(this); There is, as far as I can tell, no way to return a Promise to the event trigger call. – hyperair Jan 29 '14 at 06:16
  • Oh, I just read the answer below which mentions triggerHandler() that apparently returns the return value from the last event handler. – hyperair Jan 29 '14 at 06:24
  • 6
    "*If a single argument is passed to `jQuery.when` and it is not a Deferred or a Promise, it will be treated as a resolved Deferred and any doneCallbacks attached will be executed immediately.*" And [`trigger`](http://api.jquery.com/trigger/) **does not** return such. – Bergi Apr 30 '14 at 16:19
  • @Bergi, so then if I'm not wrong this answer does not add anything to calling sequentally `trigger` and `window.location.href = 'url' isn't? – Pherrymason Nov 07 '17 at 14:26
  • 2
    @RaúlFerràs Exactly. Using `$.when` is just plain wrong here. – Bergi Nov 07 '17 at 14:37
  • I think it is incorrect, $().trigger() do not return promise, so it won't work – Kysil Ivan Feb 07 '19 at 15:58
11

Reading documentation about triggerHandler:

Instead of returning the jQuery object (to allow chaining), .triggerHandler() returns whatever value was returned by the last handler it caused to be executed. If no handlers re triggered, it returns undefined

This point in the documentation let me think that a code like this:

// Trigger the custom event
$(this).triggerHandler('custom-event');
// This code will only run when all events are run
window.location.href = 'url';

would meet your requirements.

See http://api.jquery.com/triggerHandler/

jehon
  • 1,404
  • 15
  • 21
  • Thats true... the problem with my code was ME. =) I didn't see at the moment of posting this question that there was UI effect inside the event, and that the code was only being called from the callback of that UI effect, instead of being call directly by the vent code. – Miguel Angelo Apr 19 '13 at 18:21
  • Can you use this to trigger the handler of an event belonging to a separate iFrame? In other words, can I have iFrames communicating synchronously, and prevent further code from being processed until the response has returned? – Perry Monschau Feb 15 '17 at 23:34
  • 1
    @PerryMonschau I am pretty sure that javascript can not communicate as easilly with iframe. Take a look at serviceWorker (https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker) for inter-frame communications... Or manipulate the hash of the iFrame and watch the hash being modified back from the iframe. Or, go for a browser plugin... Anyway, AFAIK no easy solution... – jehon Feb 17 '17 at 09:14