jQuery pass more parameters into callback
Asked Answered
C

14

261

Is there a way to pass more data into a callback function in jQuery?

I have two functions and I want the callback to the $.post, for example, to pass in both the resulting data of the AJAX call, as well as a few custom arguments

function clicked() {
    var myDiv = $("#my-div");
    // ERROR: Says data not defined
    $.post("someurl.php",someData,doSomething(data, myDiv),"json"); 
    // ERROR: Would pass in myDiv as curData (wrong)
    $.post("someurl.php",someData,doSomething(data, myDiv),"json"); 
}

function doSomething(curData, curDiv) {

}

I want to be able to pass in my own parameters to a callback, as well as the result returned from the AJAX call.

Chryso answered 2/6, 2009 at 11:35 Comment(2)
It's worth mentioning that jQuery.ajax() has had a context setting since ver 1.4 (jquery14.com/day-01/jquery-14) see an example of its usage here: https://mcmap.net/q/40855/-ajax-context-option/…Justly
I soved my problem returning the data after AJAX is completed and then I doSomething.Proclamation
S
347

The solution is the binding of variables through closure.


As a more basic example, here is an example function that receives and calls a callback function, as well as an example callback function:

function callbackReceiver(callback) {
    callback("Hello World");
}

function callback(value1, value2) {
    console.log(value1, value2);
}

This calls the callback and supplies a single argument. Now you want to supply an additional argument, so you wrap the callback in closure.

callbackReceiver(callback);     // "Hello World", undefined
callbackReceiver(function(value) {
    callback(value, "Foo Bar"); // "Hello World", "Foo Bar"
});

Or, more simply using ES6 Arrow Functions:

callbackReceiver(value => callback(value, "Foo Bar")); // "Hello World", "Foo Bar"

As for your specific example, I haven't used the .post function in jQuery, but a quick scan of the documentation suggests the call back should be a function pointer with the following signature:

function callBack(data, textStatus, jqXHR) {};

Therefore I think the solution is as follows:

var doSomething = function(extraStuff) {
    return function(data, textStatus, jqXHR) {
        // do something with extraStuff
    };
};

var clicked = function() {
    var extraStuff = {
        myParam1: 'foo',
        myParam2: 'bar'
    }; // an object / whatever extra params you wish to pass.

    $.post("someurl.php", someData, doSomething(extraStuff), "json");
};

What is happening?

In the last line, doSomething(extraStuff) is invoked and the result of that invocation is a function pointer.

Because extraStuff is passed as an argument to doSomething it is within scope of the doSomething function.

When extraStuff is referenced in the returned anonymous inner function of doSomething it is bound by closure to the outer function's extraStuff argument. This is true even after doSomething has returned.

I haven't tested the above, but I've written very similar code in the last 24 hours and it works as I've described.

You can of course pass multiple variables instead of a single 'extraStuff' object depending on your personal preference/coding standards.

Solothurn answered 2/6, 2009 at 12:18 Comment(9)
What is the purpose of 'var doSomething = '? How is this different from just declaring doSomething as a function (ie function doSomething(...) {} )Intercontinental
It isn't different really. "function foo() {};" is syntactic sugar for "var foo = function() {};". That said, I tend to prefer the var syntax especially when you're passing around the function as data.Solothurn
That's not true. Different scope rules apply to these two function declarations. The behavior differs a lot depending on JS runtime (read browser). Also try to compare the function name shown in stacktrace/breakpoint in Firebug.Bisector
Ihor: You're absolutely correct about the difference between the two declaration styles. A good explanation is here: #337359Solothurn
What if I want to avoid nested functions for performance problems?Astrakhan
@IhorKaharlichenko, I understand that these two types of function declarations are different in general, but in this scenario does it make a difference with the closure? Will it work if you declare doSomething either way? I suspect it will, but I'm not sure.Koestler
If you call this ajax n times, this method creates n different functions and passes them to success callback. This is bad in terms of perfonmance. To refrain this @zeroasterisk 's answer is good.Markup
This may be a bit difficult to follow for users trying to apply this to more general problems due to the specificity of the example. I've add a simplified example make it a bit easier to follow, as well as an arrow function example to prevent this question being closed as a duplicate of the newly posted proposed duplicate. If you feel my edits deviated too far from the original intent of your answer, or are in any other way inappropriate, please feel free to roll it back.Enginery
revision 4 of this answer is being discussed on metaMarilumarilyn
V
84

When using doSomething(data, myDiv), you actually call the function and do not make a reference to it.

You can either pass the doStomething function directly but you must ensure it has the correct signature.

If you want to keep doSomething the way it is, you can wrap its call in an anonymous function.

function clicked() {
    var myDiv = $("#my-div");
    $.post("someurl.php",someData, function(data){ 
      doSomething(data, myDiv)
    },"json"); 
}

function doSomething(curData, curDiv) {
    ...
}

Inside the anonymous function code, you can use the variables defined in the enclosing scope. This is the way Javascript scoping works.

Vizierate answered 2/6, 2009 at 12:5 Comment(5)
This is the best solution IMO, for the sake of clarity. No extra return function(...){...} function within the doSomething function. I found another clear example of the same concept here: theelitist.net/…Extravagance
I think there is a problem with this one. If your extra data (myDiv in this case) changes before the callback fires, you get the new value and not the old one! In my case, I was firing off ajax calls on items in an array, and by the time the first success() call executed, it thought it had succeeded on the last item when it was the first! bradhouse's answer worked for me.Jori
@Chris Yes, captured variables can be changed in Javascript. If you don't want this behavior, you need to create an explicit function to capture the value. The most usual idiom is a self executing function (function(myDiv){ ... })(myDiv) to capture the variable myDiv. This is implicitly done in @bradhouse answer.Vizierate
If you call this ajax n times, this method creates n different functions and passes them to success callback. This is bad in terms of perfonmance. To refrain this @zeroasterisk 's answer is good.Markup
I agree with @WilliamDenniss, IMHO this is the best, simple and clear solutionDoralin
P
54

It's actually easier than everyone's making it sound... especially if you use the $.ajax({}) base syntax vs. one of the helper functions.

Just pass in the key: value pair like you would on any object, when you setup your ajax request... (because $(this) hasn't changed context yet, it's still the trigger for the bind call above)

<script type="text/javascript">
$(".qty input").bind("keypress change", function() {
    $.ajax({
        url: "/order_items/change/"+$(this).attr("data-order-item-id")+"/qty:"+$(this).val()+"/returnas.json",
        type: "POST",
        dataType: "json",
        qty_input: $(this),
        anything_else_i_want_to_pass_in: "foo",
        success: function(json_data, textStatus, jqXHR) {
            /* here is the input, which triggered this AJAX request */
            console.log(this.qty_input);
            /* here is any other parameter you set when initializing the ajax method */
            console.log(this.anything_else_i_want_to_pass_in);
        }
    });
});
</script>

One of the reasons this is better than setting the var, is that the var is global and as such, overwritable... if you have 2 things which can trigger ajax calls, you could in theory trigger them faster than ajax call responds, and you'd have the value for the second call passed into the first. Using this method, above, that wouldn't happen (and it's pretty simple to use too).

Piselli answered 8/3, 2012 at 20:36 Comment(6)
this is exactly what I was looking for. I didn't know when you specified the dataType as json it automatically parsed the response for you. That nice (:Scutcheon
Sounds like best way to pass extra params to callback to me. I would be interested if anyone sees a drawback with this approach? Easiest and most elegant. Thanks!Publicist
"the var is global and as such, overwritable". It is not if you declare it within a function, which is also much more readable than the solution above. Using closures is a cleaner approach - see my answer here: https://mcmap.net/q/109129/-jquery-pass-more-parameters-into-callbackDivulsion
@LorenzoPolidori I disagree, I find the additional parameter approach far more readable.Levis
This is absolutely brilliant. How come I never saw this before. I don't need closure I just need it to work and this is absolutely ace, clean, simple and non global.Gottfried
Really nice solution!Malines
A
21

In today's world there is a another answer that is cleaner, and taken from another Stack Overflow answer:

function clicked()
{
    var myDiv = $( "#my-div" );

    $.post( "someurl.php", {"someData": someData}, $.proxy(doSomething, myDiv), "json" );
}

function doSomething( data )
{
    // this will be equal to myDiv now. Thanks to jQuery.proxy().
    var $myDiv = this;

    // doing stuff.
    ...
}

Here's the original question and answer: jQuery HOW TO?? pass additional parameters to success callback for $.ajax call?

Appendix answered 8/9, 2011 at 15:48 Comment(0)
E
8

You can also try something like the following:

function clicked() {

    var myDiv = $("#my-div");

    $.post("someurl.php",someData,function(data){
        doSomething(data, myDiv);
    },"json"); 
}

function doSomething(curData, curDiv) {

}
Estey answered 2/6, 2009 at 12:4 Comment(0)
W
5

You can use a closure of JavaScript:

function wrapper( var1, var2,....) // put here your variables
{
  return function( data, status)
  {
     //Handle here results of call
  }
};

and when you can do:

$.post("someurl.php",data,wrapper(var1, var2, etc...),"html");
Woodcraft answered 2/6, 2009 at 12:13 Comment(0)
S
4

I've made a mistake in the last my post. This is working example for how to pass additional argument in callback function:

function custom_func(p1,p2) {
    $.post(AJAX_FILE_PATH,{op:'dosomething',p1:p1},
        function(data){
            return function(){
                alert(data);
                alert(p2);
            }(data,p2)
        }
    );
    return false;
}
Sandbag answered 3/9, 2010 at 15:25 Comment(1)
You should either edit your original answer, or delete it before adding a new one.Dasha
A
3

Let's go simple ! :)

$.ajax({
    url: myUrl,
    context: $this, // $this == Current $element
    success: function(data) {
        $.proxy(publicMethods.update, this)(data); // this == Current $element
    }
});
Anthropo answered 9/4, 2013 at 11:31 Comment(0)
D
2

A more general solution for sending asynchronous requests using the .ajax() jQuery API and closures to pass additional parameters to the callback function:

function sendRequest(method, url, content, callback) {
    // additional data for the callback
    var request = {
        method: method,
        url: url
    };

    $.ajax({
        type: method,
        url: url,
        data: content
     }).done(function(data, status, xhr) {
        if (callback) callback(xhr.status, data, request);
     }).fail(function(xhr, status) {
        if (callback) callback(xhr.status, xhr.response, request);
     });
};
Divulsion answered 2/9, 2013 at 10:26 Comment(0)
D
2

If someone still comes here, this is my take:

$('.selector').click(myCallbackFunction.bind({var1: 'hello', var2: 'world'}));

function myCallbackFunction(event) {
    var passedArg1 = this.var1,
        passedArg2 = this.var2
}

What happens here, after binding to the callback function, it will be available within the function as this.

This idea comes from how React uses the bind functionality.

Dyscrasia answered 15/10, 2016 at 8:46 Comment(0)
P
1

For me, and other newbies who has just contacted with Javascript,
I think that the Closeure Solution is a little kind of too confusing.

While I found that, you can easilly pass as many parameters as you want to every ajax callback using jquery.

Here are two easier solutions.

First one, which @zeroasterisk has mentioned above, example:

var $items = $('.some_class');
$.each($items, function(key, item){
    var url = 'http://request_with_params' + $(item).html();
    $.ajax({
        selfDom     : $(item),
        selfData    : 'here is my self defined data',

        url         : url,
        dataType    : 'json',
        success     : function(data, code, jqXHR){
            // in $.ajax callbacks, 
            // [this] keyword references to the options you gived to $.ajax
            // if you had not specified the context of $.ajax callbacks.
            // see http://api.jquery.com/jquery.ajax/#jQuery-ajax-settings context
            var $item = this.selfDom;
            var selfdata = this.selfData;
            $item.html( selfdata );
            ...
        } 
    });
});

Second one, pass self-defined-datas by adding them into the XHR object which exists in the whole ajax-request-response life span.

var $items = $('.some_class');
$.each($items, function(key, item){
    var url = 'http://request_with_params' + $(item).html();
    $.ajax({
        url         : url,
        dataType    : 'json',
        beforeSend  : function(XHR) {
            // 为了便于回调,把当前的 jquery对象集存入本次 XHR
            XHR.selfDom = $(item);
            XHR.selfData = 'here is my self defined data';
        },
        success     : function(data, code, jqXHR){
            // jqXHR is a superset of the browser's native XHR object
            var $item = jqXHR.selfDom;
            var selfdata = jqXHR.selfData;
            $item.html( selfdata );
            ...
        } 
    });
});

As you can see these two solutions has a drawback that : you need write a little more code every time than just write:

$.get/post (url, data, successHandler);

Read more about $.ajax : http://api.jquery.com/jquery.ajax/

Programmer answered 22/5, 2014 at 4:14 Comment(0)
D
1
$(document).on('click','[action=register]',function(){
    registerSocket(registerJSON(),registerDone,second($(this)));
});

function registerSocket(dataFn,doneFn,second){
                 $.ajax({
                       type:'POST',
                       url: "http://localhost:8080/store/public/register",
                       contentType: "application/json; charset=utf-8",
                       dataType: "json",
                       data:dataFn
                 }).done ([doneFn,second])
                   .fail(function(err){
                            console.log("AJAX failed: " + JSON.stringify(err, null, 2));
                        });
}

function registerDone(data){
    console.log(JSON.stringify(data));
}
function second(element){
    console.log(element);
}

Secondary way :

function socketWithParam(url,dataFn,doneFn,param){
  $.ajax({
    type:'POST',
    url:url,
    contentType: "application/json; charset=utf-8",
    headers: { 'Authorization': 'Bearer '+localStorage.getItem('jwt')},
    data:dataFn
    }).done(function(data){
      doneFn(data,param);
    })
    .fail(function(err,status,xhr){
    console.log("AJAX failed: " + JSON.stringify(err, null, 2));
    });
}

$(document).on('click','[order-btn]',function(){
  socketWithParam(url,fakeDataFn(),orderDetailDone,secondParam);
});

function orderDetailDone(data,param){
  -- to do something --
}
Diminution answered 29/4, 2016 at 6:39 Comment(0)
N
0

actually, your code is not working because when you write:

$.post("someurl.php",someData,doSomething(data, myDiv),"json"); 

you place a function call as the third parameter rather than a function reference.

Neolithic answered 2/6, 2009 at 11:53 Comment(0)
E
0

As an addendum to b01's answer, the second argument of $.proxy is often used to preserve the this reference. Additional arguments passed to $.proxy are partially applied to the function, pre-filling it with data. Note that any arguments $.post passes to the callback will be applied at the end, so doSomething should have those at the end of its argument list:

function clicked() {
    var myDiv = $("#my-div");
    var callback = $.proxy(doSomething, this, myDiv);
    $.post("someurl.php",someData,callback,"json"); 
}

function doSomething(curDiv, curData) {
    //"this" still refers to the same "this" as clicked()
    var serverResponse = curData;
}

This approach also allows multiple arguments to be bound to the callback:

function clicked() {
    var myDiv = $("#my-div");
    var mySpan = $("#my-span");
    var isActive = true;
    var callback = $.proxy(doSomething, this, myDiv, mySpan, isActive);
    $.post("someurl.php",someData,callback,"json"); 
}

function doSomething(curDiv, curSpan, curIsActive, curData) {
    //"this" still refers to the same "this" as clicked()
    var serverResponse = curData;
}
Encomiastic answered 8/10, 2011 at 18:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.