2
\$\begingroup\$

What I want to do is; without leaving the chain, execute some custom/external code (and continue) within the chain. Otherwise; in a deep chain, I will have to re-query for the DOM element to get back where I've left.

The method below allows to execute custom code inside a jQuery chain synchronously or asynchronously.

//Execute code inside a jQuery chain
$.fn.do = function (callback, async) {
    if (typeof callback === 'function') {
        if (async === true) {
            this.queue(function() {
                callback($(this));
                $(this).dequeue();
            });
        } else {
            callback(this);
        }
    }
    return this;
};

Simplified Usage Example:

var results = {};
$('#some-elem')
    .css({
        //modify some styles here
    })
    .do(function($elem) {
        //do some calculations.. tests here..
        //save them to results object
    }, false) //synchronous
    .css({
        //re-set the styles back to their initial state
    });

This could be a longer chain where you could need to enter the do() method multiple times.

So;

  • Do you think the approach above is legitimate?
  • Would you suggest an alternate? Why?
\$\endgroup\$
11
  • \$\begingroup\$ I'm not sure about the necessity of .queue() here - why are you adding a function to the default fx queue (will be queued after animations when there are pending animations) if this version of the method is asynchronous? You can probably get rid of that. \$\endgroup\$ Mar 12, 2013 at 0:28
  • \$\begingroup\$ @FabrícioMatté This can be used for many purposes (an external operation or a related operation set to occur after some animation that may need the gathered data inside the do() method) so I'm allowing for both async and immediate execution. (I'm also passing the corresponding jQuery element as an argument.) Does it make sense? \$\endgroup\$ Mar 13, 2013 at 1:04
  • \$\begingroup\$ I guess. Let me see, in your example code, the last .css() should wait until do() calls dequeue()? \$\endgroup\$ Mar 13, 2013 at 1:29
  • \$\begingroup\$ The example above is completely synchronous since .css() method is executed immediately and .do() is called with false param. And essentially, do(func, true) (async) is only a shorhand for queue() which will execute when it's his turn in the queue. You might say that I'm only extending the .queue() method with a sync execution capability. \$\endgroup\$ Mar 13, 2013 at 2:13
  • \$\begingroup\$ If your intent is to extend queue then this looks perfectly fine. I may take another look tomorrow but there isn't much code to cut out without losing a lot of readability. \$\endgroup\$ Mar 13, 2013 at 2:15

1 Answer 1

2
\$\begingroup\$

I guess this plugin could be useful, however I think you can drop the async part since it's not any harder to call queue directly.

Also, I am not a huge fan of error swallowing or ignoring invalid calls since it makes the code harder to debug. If there's a call to do without providing a callback, you should let the developer be notified.

I also like to allow defining the this value for the callback function so that you do not need to use $.proxy for that purpose.

Finally I allowed to return a value from the callback to change the target object for the rest of the chain. However I am not so sure about this feature since it could harm code comprehension but I am leaving it there as an idea.

Basically it would be as simple as:

!function($) {
    $.fn.do = function (callback, thisArg) {
        if (typeof callback !== 'function') {
            throw new TypeError("the 'callback' argument must be of type 'function'");
        }

        return callback.call(thisArg || this, this) || this;
    };
}(jQuery);

Note: I've defined the plugin within an IIFE so that it still works if $.noConflict() was used.

\$\endgroup\$
1
  • \$\begingroup\$ +1 for thisArg. Though, I wouldn't modify the returned object since this is for keeping the chain. Sure you can call queue()' easily; async` acts like a shortcut there. Thanks. \$\endgroup\$ Oct 20, 2013 at 15:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.