简体   繁体   中英

Best way to define a callback and fire it once immediately?

In jQuery, I do stuff like this a lot:

$('#myId').bind('myevent', function() { ... }).trigger('myevent');

Works great when the selector only finds one element, but I don't want it to fire more than once if there's more than one match.

Is there some way to basically declare an annonymous function and execute it exactly once all in one swoop?

Or do you have to make it non-annonymous (give it a name), pass it in, and call it once manually?


For example, I might bind a .change event to set of radio buttons. In the callback, I check which one is actually set and then show/hide a div. When the page loads, the div needs to be in the correct state, so I fire the event once and let the JS figure out whether or not it should be shown. But I don't really want it to fire 3 times because I've got 3 radio buttons.

Use the triggerHandler() [docs] method instead of the trigger() [docs] method.

This will invoke the handler only once, and will not allow the event to bubble or trigger the default behavior.

From the docs:

The .triggerHandler() method behaves similarly to .trigger(), with the following exceptions:

  • The .triggerHandler() method does not cause the default behavior of an event to occur (such as a form submission).
  • While .trigger() will operate on all elements matched by the jQuery object, .triggerHandler() only affects the first matched element.
  • Events created with .triggerHandler() do not bubble up the DOM hierarchy; if they are not handled by the target element directly, they do nothing.
  • 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 are triggered, it returns undefined

If the only issue is when the selector matches more than one element, just use .first() :

$('some selector').bind('myevent', function()
{
    // ...
}).first().trigger('myevent');

It would be very straightforward to turn this into a jQuery plugin, which you'd use just like .bind() :

$('some selector').bindTrigger('myevent', function () { ... });

Plugin code (not tested)

;(function ($)
{
    $.fn.bindTrigger = function ()
    {
        this.bind.apply(this, arguments).first().trigger(arguments[0]);
    };
})(jQuery);

I can think of two ways to define an anonymous function and call it at the same time.

The first is to make the function return itself using the arguments.callee value and invoke the function at that place. An example probably makes it clearer:

el.bind('myevent', (function() {
  ...;
  return arguments.callee; // return the function
})());

The problem with this first approach is, that the function always returns itself. Another approach is to extend the Function.prototype with a helper method as in the following example demonstrates:

// extend the prototype once somewhere
Function.prototype.invoke = function(){
  this();      // invoke...
  return this; // and return the function
};

// invoke() on an anonymous function now calls the function
// and returns the function instead of the result.
el.bind('myevent', (function(){
  ...
}).invoke());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM