How do I specify more arguments to be passed to a jsonp callback function?
For example, I'm trying to grab youtube video data:
http://gdata.youtube.com/feeds/api/videos/gzDS-Kfd5XQ?v=2&alt=json-in-script&callback=youtubeFeedCallback
The javascript callback function that will be called is youtubeFeedCallback and it contains only one argument when called.
As of now the function would be something like this,
function youtubFeedCallback(response) {
...
}
What I would like to be able to do is pass a second argument like this,
function youtubeFeedCallback(response, divId) {
...
}
Is this possible to do. I've tried looking everywhere online and couldn't find anything.
Thanks!
You can't add arguments to the callback function like that. However, you can generate a wrapper function. The JSONP callback function just was to be a function in the default namespace, that means that you just need to add a generated function with a known name to the global window object. Step one is to make up a name:
var callback_name = 'youtubeFeedCallback_' + Math.floor(Math.random() * 100000);
In the real world you'd want to wrap that in a loop and check that window[callback_name] isn't already taken; you could use window.hasOwnProperty(callback_name) to check. Once you have a name, you can build a function:
window[callback_name] = function(response) {
youtubeFeedCallback(response, divId);
};
You'd want to that up a little bit more though:
function jsonp_one_arg(real_callback, arg) {
// Looping and name collision avoidance is left as an exercise
// for the reader.
var callback_name = 'jsonp_callback_' + Math.floor(Math.random() * 100000);
window[callback_name] = function(response) {
real_callback(response, arg);
delete window[callback_name]; // Clean up after ourselves.
};
return callback_name;
}
Once you have something like that wired up, you could just call:
jsonp = jsonp_one_arg(youtubeFeedCallback, divId);
And then use the value of jsonp as the callback value in the YouTube URL.
You could build more functions like this to handle longer arguments lists too. Or you could build a general purpose one with arguments and apply.
better way is specify an associated array of divId and videoId like this
var arr = {
'gzDS-Kfd5XQ': 'divId_1',
'gwWS-Gasfdw': 'divId_2'
};
and in callback function get your divId by videoId
function youtubFeedCallback(data)
{
var divId = arr[data.entry.media$group.yt$videoid.$t];
}
When creating a "script" object, you can add attributes to it, and reference them later with document.currentScript. This worked for me but other people may need something more elegant.
function blah(stuff,extraarg){
cs=document.currentScript;
console.log(stuff); // stuff is your json output
console.log(cs.extraarg); // cs.extraarg is your extra arg
}
function thing(extraarg){
var url='https://....&json_callback=blah';
var s=document.createElement('script');
s.src=url;
s.extraarg=extraarg;
document.body.appendChild(s);
}
Related
I have seen several examples of java script functions with parameters passed which are not located in the script but are implicitly passed in. For example:
function myFunction(xml) {
var xmlDoc = xml.responseXML;
document.getElementById("demo").innerHTML =
xmlDoc.getElementsByTagName("title")[0].childNodes[0].nodeValue;
}
Where is the "xml" defined or listed? Where can i find a listing of other implicit parameters?
I've also seen a function with the following:
$("body").click(function (event) {
// Do body action
var target = $(event.target);
if (target.is($("#myDiv"))) {
// Do div action
}
});
Where is "event" coming from or listed?
Thanks in advance.
These variables are called (function) parameters. This is a common feature of most programming languages. They are defined with a function, and simply serve as variables that are defined within the function. They do not need to be defined outside of the function previously, because they exist only for the function.
I believe you're confused because they're not declared with var (as they shouldn't be) because you're calling them "implicit variables." However, they are not implicit; they are defined with the function.
You can find the parameters to a function by looking at the documentation for the function, if you are using a library like jQuery. For example, the .click() function handler is defined like:
(If you can't see the image, it shows .click(handler), where handler is of Type: Function(Event eventObject))
As you can see, it defines the function parameter eventObject which you can "pass" in when you invoke a function. You can use any valid variable name to do so.
You can see this MDN documentation for more information on parameters.
Where is the "xml" defined or listed? Where can i find a listing of other implicit parameters?
Is listed in the very function definition. When I define a function like:
function greet( name , greeting ){
console.log('hi ' + name );
console.log(greeting);
}
name and greeting vars are just defined within the parenthesis in the function definition. You can just call that function passing literals or variables:
greeting('peter' , 'have a nice day');
//or:
var name = 'Francisco';
var greeting = 'qué pasa hombre';
greet(name , greeting);
In the second example, name and greeting vars happen to be called exactly like the internal function parameters. That is just by case, could be too:
var theAame = 'Francisco';
var theGreeting = 'qué pasa hombre';
greet(theName , theGreeting);
And would work exactly the same. Also, in javaScript, you can just pass more parameters to a function than the parameters actually defined in the function, and access them with the arguments keyword or the ES6 spread syntax.
This is javaScript bread and butter and any search on how does arguments and parameters work in javaScript will be useful to you. However, your second question is more tricky.
You're also asking about this kind of code:
$("body").click(function (event) {
// Do body action
var target = $(event.target);
if (target.is($("#myDiv"))) {
// Do div action
}
});
This is similar, but also is more complex. Here $("body").click() is a function that takes a parameter. This parameter happens to be another function. This is a feature not supported in some languages, but is pretty straightforward in javascript. You could also wrote that code this way:
function reactToClick (event) {
// Do body action
var target = $(event.target);
if (target.is($("#myDiv"))) {
// Do div action
}
}
$("body").click( reactToClick );
But, who is then calling that reactToClick function with that event parameter? Well, in this case, the browser does.
Any browser has some API's to register to events -like clicks- with function callbacks, and $.click() is just some syntactic helper over that mechanism. Since is the browser who is ultimately calling the function, is difficult to fully understand the internals -and I must admit I don't-.
However, you can set up your own non-browser-api-dependant javaScript code that invoke callbacks, and the parameters set up and function invocations works the same way:
function theCallback( name , options ){
console.log('Im a callback function fired by someCallbackRegister whenever its fire methods is called');
console.log('my callbackRegister name is: ' + name);
console.log('and the options provided in this call are: ' + options);
}
function someCallbackRegister( callback , registerName ){
return {
fire : function(options){
callback(registerName , options );
}
}
}
var listener = someCallbackRegister( theCallback , 'Johhny');
listener.fire({ foo : 'bar'});
In this example, is the listener who is invoking theCallback after it's fire method call, and setting up all the parameters to that theCallback function properly, just like the browser manages to pass an event object to the callback function you pass to $.click().
Hope this helps :-)
PS: This video about the javaScript event loop helped me a lot to understand how the browser api's work.
function myFunction(xml) {
}
Whoever invokes this myFunction will pass the details which will be saved to variable xml. It's JS language syntax - you don't need to define the type of variable here unlike Java.
Similarly, when you do this:
$("body").click(function (event) {
});
JS internally registers a callback method whenever the body is clicked. It internally passes the event details to the function. You can do console.log(event) and see what all details are listed there
Ok, I wouldn't think to do this in C#, but javascript is designed with much more flexibility in access.
there's a plugin like this
(function($)
{
...more stuff
var results={a:1,b:2} //this I need to modify
var someData={x:1}
send = function(){
//send results ajax
};
if(typeof beforeSend=='function')
beforeSend(someData) //hook to use results
})(jQuery)
So, in my own code, I have the function window.beforeSend = function(d){}
and it does have the someData which is in the scope I need to modify. But here's the question:
How can I modify the results var that's within the closure before it sends it.
I need to add
window.beforeSend = function(d){
window.quantumTunnelThroughScope.results['c']=1
}
The reason I need to do this is because I cannot modify the code of the plugin. Of course if I add the beforeSend within the closure, it works, but then I'm modifying the library which I'm not allowed to do in this case.
I've seen some awesome eval('this.xx' =function ) etc etc but I can't make it work.
EDIT: I clarified that actually it's a different var in the same scope that needs to be edited
No, there's no reasonable way for beforeSend to reach into that closure and modify results. results in the code presented is entirely private to code within that closure.
The unreasonable way to try to do it is to decompile and recompile the plugin function, via eval, and insert a call to a function before the beforeSend that lets us modify results:
(function($) {
$.run = function() {
// You mentioned "ajax," so let's make this
// asynchronous
setTimeout(function() {
var results = {
a: 1,
b: 2
};
var someData = { // Need to modify this
x: 1
};
send = function() {
//send results ajax
};
if (typeof beforeSend == 'function') {
beforeSend(someData); //hook to use results
}
console.log("in plugin, results = ", results);
}, 10);
};
})(jQuery)
window.modifyResults = function(d) {
return ["new", "results"];
};
window.beforeSend = function(r) {
r.c = 1;
};
jQuery.run = (function() {
// Function#toString, on nearly all browsers, returns the source
// code of he function (or something near to it) except on functions
// implemented in native code. We take that string and replace
// the "beforeSend(someData);" call with two calls, the first of
// which lets us modify the `results` variable. Then we use eval
// to turn that back into a function, and assign the result to
// where the plugin put its function originally.
return eval("(" + jQuery.run.toString().replace(
"beforeSend(someData);",
"results = modifyResults(results); beforeSend(someData);"
) + ")");
})();
jQuery.run();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
But may or may not work, depending on how the plugin is written, as it lifts it out of its original scope and recompiles it in the scope of our function updating jQuery.run.
I think I'd prefer to take the hit of modifying the plugin. :-)
Note: In the above, I've used a "static" jQuery function. If the plugin you're replacing provides an instance function, the kind you can call on jQuery instances, e.g. the bar in $(".foo").bar(), you'll find it on jQuery.fn instead of jQuery:
jQuery.fn.pluginFunction = eval(...);
I've heard that it's possible to do something like this
this[func].apply(this, Array.prototype.slice.call(arguments, 1));
But to have access to the arguments object I need to be inside that function.
So, if I am, for example, running a code in the function function1, is there a way to get the arguments of a different function function2?
The whole problem is that I want to add events to any given element. But this element might already have another event attached to it. so, if I have, for example, an 'onclick' event attached to an element, I would do the following:
var event = 'onclick';
if(typeof currentElement[event] === 'function'){
cf = currentElement[event];
f = function(){
cf();
func();
}
}else f = func;
currentElement[event] = f;
Now both functions, the new function and the previous function, are being called. The problem is that the arguments being passed to the previous function were lost with this method.
Does anyone know if it is possible to not lose the arguments when we call a function dynamically like in my example?
OBS: jQuery is not an option :/
Sounds like using addEventListener would work better for you. It lets you attach multiple listeners (functions) to a single element:
elem.addEventListener("click", function() {
console.log("click listener 1");
});
elem.addEventListener("click", function() {
console.log("click listener 2");
});
Note, according to MDN, addEventListener is supported in IE >= 9.
If you did want to continue down your current path, you could do:
f = function(){
cf.apply(this, arguments);
func.apply(this, arguments);
}
For you specific case, it's not necessary to figure out what arguments are passed to the function since you know what it is - the event object in all browsers except (older) IE where its nothing (undefined).
So the code can simply be:
var event = 'onclick';
if(typeof currentElement[event] === 'function'){
cf = currentElement[event];
f = function(e){
cf(e);
func(e);
}
} else f = func;
currentElement[event] = f;
If possible use addEventListner and attachEvent as fallback to play nicely with other scripts that may run on your page.
Additional discussion:
In general, it is never necessary to figure out what has been passed to the callback function because that doesn't even make sense. The programmer cannot determine what to pass to the callback, it's the event emitter that determines what gets passed. In this case its the event object but it's a general rule.
Say for example we have an API that queries our database and returns some data:
function my_api (query) {}; // returns an object where a callback may
// be attached to the oncomplete property
The API documentation mentions:
my_api() returns an API object.
API object - has one property - oncomplete where a callback may be attached to handle data returned from the api call. The callback will be called with one argument - the returned data or no argument (undefined) if an error occured.
OK. So we use it like this:
var db = my_api('get something');
db.oncomplete = function (data) {alert(data)};
Now, if we want to wrap that oncomplete event in another event handler, we don't need to know what data it accepts since it's the API object that determines what to pass to the function, not the function! So we simply:
var tmp = db.oncomplete;
db.oncomplete = function (x) {
new_callback(x);
tmp(x);
}
We get the x argument not from querying the previous callback but from the documentation.
somefunction.arguments //Use the arguments property to get arguments of another function
I have an object which can only place 60 API calls per minute. So what I would like to do, is when a function call comes that I know I wont be allowed to place, add it to a queue, and call the function again at a more convenient time.
Heres how I thought to fix it
var API_caller = function(){
this.function_queue = [];
};
API_caller.prototype.make_api_call = function(){
if(this.can_make_call()){
this.make_call();
} else {
// If I cant place an API call then add
// the function to the function queue
this.function_queue.push(this.make_api_call);
}
};
API_caller.prototype.queue_call = function(){
// remove function from queue and call it
var func = this.function_queue.shift();
func();
}
This works fine for functions without parameters but what if make_api_call() had a parameter
API_caller.prototype.make_api_call = function(data){
if(this.can_make_call()){
this.make_call();
} else {
// If I cant place an API call then add
// the function to the function queue
this.function_queue.push(this.make_api_call(data));
}
};
In this case however, make_api_call(data) will be evaluated before it is pushed to function_queue and func will no longer hold a function causing queue_call() to error.
How can I get around this?
You can partially apply arguments to a function with bind:
this.function_queue.push(this.make_api_call.bind(this, data));
Check MDN for support in old browsers.
The queue entry should contain the function, f and the parameters as an array, p.
When you add to the queue you would do something like queue.push ([f, arguments]) and when the time comes to make that call it would be something like queue[0][0].apply (null, queue[0][1])
You can queue up an ad-hoc function that contains your API call with the argument already bound:
var that = this;
this.function_queue.push(function() {
that.make_api_call(data);
));
The aliasing of this to that is required, because inside the anonymous function, this would not be bound to the same object as outside.
Note this technique is similar to eclanrs' answer, but doesn't rely on bind method's availability.
I'm trying to build an API in JS that will perform some operations and then execute the callback that's registered in AS when it's done. Because it's an API, I am just providing a JS method signature for another developer to call in Flash. Thus, the callback name that's registered in the AS part of the code should be a parameter that's passed in to the JS API in order for JS to communicate back to Flash.
For example:
[AS3 code]
ExternalInterface.addCallback("flashCallbackName", processRequest);
ExternalInterface.call("namespace.jsFnToCall", flashCallbackName);
function processRequest(data:String):void
{
//do stuff
}
[JS code]
var namespace =
{
jsFnToCall: function(callback)
{
//Do stuff in this function and then fire the callback when done.
//getFlashMovie is just a util function that grabs the
//Flash element via the DOM; assume "flash_id"'s a global var
//Below does not work...it's what I'd be ideally be doing some how.
getFlashMovie(flash_id).callback(data);
}
};
Because the definition of the function is in AS, I can't use the window[function name] approach. The only way I can think of is to build the callback in a string and then use the eval() to execute it.
Suggestions? T.I.A.
Well, I can think of one thing I would try, and one thing that would work.
What I would try first.
getFlashMovie(flash_id)['callback'](data);
What would work: Have the callback always be the same, say callback. The first parameter to the callback could be used to determine what actual function to call in flash. For example:
function callback($fn:String, $data:*) {
// either
this[$fn]($data);
// or
switch ($fn) {
case "callback1":
DoSomeCallback($data);
break;
}
Additionally passing the objectID makes it a bit simpler:
ExternalInterface.addCallback("flashCallbackName", processRequest);
ExternalInterface.call("namespace.jsFnToCall", ExternalInterface.objectID, "flashCallbackName");
Then in your JS:
var namespace =
{
jsFnToCall: function(objectID, callback)
{
//Do stuff in this function and then fire the callback when done.
document[objectID][callback](data);
}
};