The documentation of jQuery's hover shows only one method of using the function:
$('.myClass').hover(function () {
console.log('on mouse over');
},
function () {
console.log('on mouse out');
});
However, when you change these to named functions it doesn't work correctly, firing the named functions upon page load (or as soon as you paste it into your console):
function onMouseOver() {
console.log('on mouse over');
}
function onMouseOut()
console.log('on mouse out');
}
$('.myClass').hover(onMouseOver(), onMouseOut());
Changing the last line to:
$('myClass').hover(onMouseOver, onMouseOut);
works as expected (firing on the event), but doesn't allow me to pass anything to the named functions. Is there any way to allow me to pass a variable to the functions?
Yes you need to use anonymous functions for this:
$('myClass').hover(function( e ) {
onMouseOver( param1, param2... );
}, function( e ) {
onMouseOut( param1, param2... );
});
You can pass variables into the named functions by calling it like so:
$('.myClass').hover(function() {
onMouseOver(arg);
}, function() {
onMouseOut(arg);
});
That's the only way to pass arguments, parameters into the named functions from that event.
The hover sugar method isn't really meant for complex scenarios.
In your case it would probably be better to use .on('mouseenter') and on('mouseleave') so that you can pass additional event data to each method, like
$('.myClass').on('mouseenter', {param1: val1}, onMouseOver).on('mouseleave', {param2: val2}, onMouseOut);
Then within your handlers you can access those params like so:
function onMouseOver(e) {
console.log(e.data.param1);
}
function onMouseOut(e) {
console.log(e.data.param2);
}
That's the sort of jQuery way to do it.
This is a problem with function references vs. function invocation. Adding the "()" invokes the function (which in this case you'd be doing at binding time...effectively binding the result of the function rather than the function itself.
To pass arguments the simplest option would be to wrap the named function in an anonymous function (as #antyrat just posted).
And also no, this is not a quirk of hover, this is standard JavaScript (and most any other language that has first class functions).
As several others have noted, you'll have to use currying or binding to pass values to the functions. In the example where you wrote this:
$('.myClass').hover(onMouseOver(), onMouseOut());
you're actually calling the onMouseOver() and onMouseOut() methods immediately on that line, and not when the mouse actually moves over or out of the element; what you wrote is equivalent to writing this:
var mouseOverResult = onMouseOver();
var mouseOutResult = onMouseOut();
$('.myClass').hover(mouseOverResult, mouseOutResult);
That's definitely not what you want.
jQuery can only understand functions that are of the form function(event), so if you want more parameters, or other parameters, you'll have to use currying to get them in there. Currying (named for the math professor who devised the concept) can be thought of as creating a new function where the values you want to pass are 'pre-bound' inside it.
So let's say you have a variable foo that you'd like to pass into your onMouseOver handler, like this:
function onMouseOver(foo) {
...
}
...
var foo = "Hello, World";
$('myClass').hover(...);
To be able to pass that value, you need another function that wraps up that foo and that onMouseOver with a function signature that jQuery can use. You do it like this:
function onMouseOver(foo) {
...
}
...
var foo = "Hello, World";
var curriedOnMouseOver = function(event) {
onMouseOver(foo);
};
$('myClass').hover(curriedOnMouseOver);
As several others have suggested, you can avoid the extra variable declaration by creating the curried closure inside the hover() call:
function onMouseOver(foo) {
...
}
...
var foo = "Hello, World";
$('myClass').hover(function(event) {
onMouseOver(foo);
});
This example above also shows how you would pass the event to your function as well, by simply adding more parameters to it:
function onMouseOver(event, foo, bar) {
...
}
...
var foo = "Hello, World";
var bar = "Goodbye, World";
$('myClass').hover(function(event) {
onMouseOver(event, foo, bar);
});
JavaScript's functions --- or closures --- are incredibly powerful tools, and it would be worth your while to learn some of the things you can do with them, like these examples.
Related
I want to write my own function in JavaScript which takes a callback method as a parameter and executes it after the completion, I don't know how to invoke a method in my method which is passed as an argument. Like Reflection.
example code
function myfunction(param1, callbackfunction)
{
//do processing here
//how to invoke callbackfunction at this point?
}
//this is the function call to myfunction
myfunction("hello", function(){
//call back method implementation here
});
You can just call it as a normal function:
function myfunction(param1, callbackfunction)
{
//do processing here
callbackfunction();
}
The only extra thing is to mention context. If you want to be able to use the this keyword within your callback, you'll have to assign it. This is frequently desirable behaviour. For instance:
function myfunction(param1, callbackfunction)
{
//do processing here
callbackfunction.call(param1);
}
In the callback, you can now access param1 as this. See Function.call.
I too came into same scenario where I have to call the function sent as parameter to another function.
I Tried
mainfunction('callThisFunction');
First Approach
function mainFuntion(functionName)
{
functionName();
}
But ends up in errors. So I tried
Second Approach
functionName.call().
Still no use. So I tried
Third Approach
this[functionName]();
which worked like a champ. So This is to just add one more way of calling. May be there may be problem with my First and Second approaches, but instead googling more and spending time I went for Third Approach.
function myfunction(param1, callbackfunction)
{
//do processing here
callbackfunction(); // or if you want scoped call, callbackfunction.call(scope)
}
object[functionName]();
object: refers to the name of the object.
functionName: is a variable whose value we will use to call a function.
by putting the variable used to refer to the function name inside the [] and the () outside the bracket we can dynamically call the object's function using the variable. Dot notation does not work because it thinks that 'functionName' is the actual name of the function and not the value that 'functionName' holds. This drove me crazy for a little bit, until I came across this site. I am glad stackoverflow.com exists <3
All the examples here seem to show how to declare it, but not how to use it. I think that's also why #Kiran had so many issues.
The trick is to declare the function which uses a callback:
function doThisFirst(someParameter, myCallbackFunction) {
// Do stuff first
alert('Doing stuff...');
// Now call the function passed in
myCallbackFunction(someParameter);
}
The someParameter bit can be omitted if not required.
You can then use the callback as follows:
doThisFirst(1, myOtherFunction1);
doThisFirst(2, myOtherFunction2);
function myOtherFunction1(inputParam) {
alert('myOtherFunction1: ' + inputParam);
}
function myOtherFunction2(inputParam) {
alert('myOtherFunction2: ' + inputParam);
}
Note how the callback function is passed in and declared without quotes or brackets.
If you use doThisFirst(1, 'myOtherFunction1'); it will fail.
If you use doThisFirst(1, myOtherFunction3()); (I know there's no parameter input in this case) then it will call myOtherFunction3 first so you get unintended side effects.
Another way is to declare your function as anonymous function and save it in a variable:
var aFunction = function () {
};
After that you can pass aFunction as argument myfunction and call it normally.
function myfunction(callbackfunction) {
callbackfunction();
}
myfunction(aFunction);
However, as other answers have pointed out, is not necessary, since you can directly use the function name. I will keep the answer as is, because of the discussion that follows in the comments.
I will do something like this
var callbackfunction = function(param1, param2){
console.log(param1 + ' ' + param2)
}
myfunction = function(_function, _params){
_function(_params['firstParam'], _params['secondParam']);
}
Into the main code block, It is possible pass parameters
myfunction(callbackfunction, {firstParam: 'hello', secondParam: 'good bye'});
callbackfunction = () => {}
callbackfunction2(){
}
function myfunction1(callbackfunction) {
callbackfunction();
}
//Exe
myfunction1(callbackfunction);
myfunction1(callbackfunction2.bind(this));
Super basic implementation for my use case based on some excellent answers and resources above:
/** Returns the name of type member in a type-safe manner. **(UNTESTED)** e.g.:
*
* ```typescript
* nameof<Apple>(apple => apple.colour); // Returns 'colour'
* nameof<Apple>(x => x.colour); // Returns 'colour'
* ```
*/
export function nameof<T>(func?: (obj: T) => any): string {
const lambda = ' => ';
const funcStr = func.toString();
const indexOfLambda = funcStr.indexOf(lambda);
const member = funcStr.replace(funcStr.substring(0, indexOfLambda) + '.', '').replace(funcStr.substring(0, indexOfLambda) + lambda, '');
return member;
}
I'm having some trouble with JavaScript and the passing of a function as parameter of another function.
let's say we are inside a class and do something like that:
this.get('target').update(this.onSuccess, this.onFail);
'target' is a JavaScript-object that has a method called update()
I'm calling this method and pass tow methods of the caller-class along as parameters
inside that update-method some stuff happens and when it's done that method should either call the onSuccess-method or the onFail-method. this looks something like:
update: function(onSuccess, onFail) {
if(true) {
onSuccess();
} else {
onFail();
}
}
until now, everything works pretty fine! but inside those success/fail-methods, that are defined in the caller-class (the one that calls above update-method), I'm using a this-pointer:
onFail: function() {
alert('Error: ' + this.get('target').error);
}
that this-pointer causes some issues. it doesn't point to the class where the method initially was defined but to the 'target'-object.
what I need to do now is to update the this-pointer right before the onSuccess / onFail calls inside the 'target'-class to make the methods work again. but this doesn't work due to a 'invalid assignment left-hand side'-error.
what is the best practice for a scenario like that? any ideas? thx in advance!!!
cheers
You have two options when calling update():
javascript function call()
javascript function apply()
the main difference being how you pass parameters to it. But they both allow scope/context injection.
Your code should look something like this:
this.get('target').update.call(this, this.onSuccess, this.onFail);
You can create a function that "binds" a function to a certain object (using a closure) and than pass these bound functions to the handler:
function bind(obj, fun) {
return function() {
return fun.apply(obj, arguments);
};
};
update(bind(this, this.onSuccess), bind(this, this.onFail));
To redirect this, you need a bind() (or similar) method in the Function class, as found in almost all JavaScript libraries:
if(!Function.prototype.bind){
Function.prototype.bind = function(scope) {
var _function = this;
return function() {
return _function.apply(scope, arguments);
}
}
}
Now do something like this:
update: function(onSuccess, onFail) {
if(true) {
onSuccess.bind(this)();
} else {
onFail.bind(this)();
}
}
The mechanism is explained here: Binding Scope in JavaScript
What is the difference of calling function like:
testCall: function() and function testCall() in jQuery ?
Update:
Questions: Does usage of one over the another have some performance issues related to it OR it really does not matter which one you are using ?
Update 2
Also other thing that I noticed that whenn I am defining function using testCall: function() and I call it using this.testCall() it works fine and am able to call it in any other function.
But when I am using function testCall() and I try to call it using testCall() in another function than I am getting errors and am not able to call it. Is this possible or there could be some other reason for the errors ?
In this example:
testCall: function()
testCall is now a function available on the object you're in, like this: object.testCall() It can access other functions, properties, etc inside this object if it needs to.
In this version:
function testCall()
testCall is just a globally available method, not scoped to the object or plugin, whatever you're dealing with, you can call it from anywhere, like this: testCall()
This is really a question about Javascript syntax (and semantics), not jQuery.
Both of those constructions define functions. This:
var x = {
// ...
name: function() { /* ... */ },
// ...
};
defines a function (an anonymous function) and assigns it as the value of the property called "name" in the object being assigned to the variable "x".
This:
function name() {
/* ... */
}
defines a function with the name "name". The effect is similar to:
var name = function() { /* ... */ };
but definitely different. However, for most purposes it's safe to think about them as being almost the same. The effect is that "name" is bound to the function in the lexically-enclosing scope. If you do that definition outside of any other function, then "name" becomes a property of the "window" object, and the function is therefore globally available. If that declaration is inside another function, then "name" is only available inside that function.
Usually you see the first form when you're doing something like setting up callbacks for some jQuery facility, like a UI plugin or $.ajax. You're giving jQuery a function that it should call upon something happening — an AJAX call finishing, or a use action like a mouse click, or completion of some sort of animation.
edit oh, and finally here's another note. If you define a function the second way, well then you can refer to that function by name and use it in an object definition (like the first example):
function globalFunction() {
function localFunction() { /* ... */ };
jQuery.something({
x: 100, y: 100,
callback: localFunction,
// ...
});
}
Many more such things are possible - functions are values in Javascript and can be tossed around as easily as numbers and strings.
The first (testCall: function()) is object literal notation for defining a function and assigning it to a property on an object (not shown). The function itself is anonymous; the property it is bound to has a name, but the function does not.
The second (function testCall()) is a named function.
Named functions have several advantages over anonymous ones, and so though you see the first format quite a lot, I would recommend using it sparingly if at all. Named functions can be reported usefully by your tools (debuggers and the like), whereas anonymous functions just show up as ? or (anonymous). More here.
For that reason, rather than this:
function doSomeNiftyAjaxyThing(foo) {
$.ajax({
url: "blah",
success: function() {
// Do something involving the successful result and `foo`
foo.bar();
}
});
}
I would typically do this instead:
function doSomeNiftyAjaxyThing(foo) {
$.ajax({
url: "blah",
success: niftySuccess
});
function niftySuccess() {
// Do something involving the successful result and `foo`
foo.bar();
}
}
Not only does this keep my code a bit cleaner (er, to my mind), but it means that if something goes wrong inside niftySuccess, I've given the function a name my tools can report to me. Note that other than the fact that the function has a name, they're identical – both functions are closures over the foo argument and anything else inside doSomeNiftyAjaxyThing.
You might be tempted to give the function a name inline, like so:
function doSomeNiftyAjaxyThing(foo) {
$.ajax({
url: "blah",
success: function niftySuccess() { // <== change here, PROBLEMATIC
// Do something involving the successful result and `foo`
foo.bar();
}
});
}
There you're declaring a function with a name as an expression, and assigning the result of the expression to a property. Arguably you should be able to do that, but there are a series of implementation...anomalies in the various Javascript engines out there that prevent your being able to do that. (More in the article linked above, and in this article.)
How do I make the myFunction visibile for the in-line function in .ready() event?
$(document).ready(function() {
...stuffs...
myFunction(par1, par2, anotherFucntion_callback);
}
);
function anotherFunction_callback(data) {
..stuffs..
}
I didn't quite catch your question. Do you mean that you want to pass "myFunction_callback(data)" as the last argument in your:
myFunction(par1, par2, anotherFunction_callback);
, including that "data" parameter?
In that case the solution is pretty standard, write this before that one:
var temp = function() { anotherFunction_callback(data) };
an alternative syntax is:
function temp() { myFunction_callback(data) };
// even though this looks just like a free function,
// you still define it inside the ready(function())
// that's why I call it "alternative". They are equivalent.
In general, if you want to pass a function with 1 or more arguments to another function, you use that format. Here, we basically create a new no-argument function that calls another. The new function has access to the "data" variable. It's called "closure", you may want to read more on that.
Of course, if the callback require no argument, you can just use the original function name.
I hope this helps.
ps: You can even inline the function declaration, making it anonymous, like so:
myFunction(par1, par2, function() { myFunction_callback(data) });
Notice that the
$(document).ready(function() {});
looks pretty much just like that.
You use the actual name of the function, i.e. myFunction_callback instead of myFunction or anotherFucntion_callback.
I have an onclick handler for an <a> element (actually, it's a jQuery-created handler, but that's not important). It looks like this:
function handleOnClick() {
if(confirm("Are you sure?")) {
return handleOnClickConfirmed();
}
return false;
}
From this function, the this object is accessable as the <a> element clicked. However, handleOnClickConfirmed's this is a Window element! I want handleOnClickConfirmed to have the same this as handleOnClick does. How would I do this?
(I know I can pass this as an argument to handleOnClickConfirmed, but some of my code already uses handleOnClickConfirmed and I don't want to have to rewrite those calls. Besides, I think using this looks cleaner.)
The following ought to do it:
function handleOnClick() {
if( confirm( "Sure?" ) ) {
return handleOnClickConfirmed.call( this );
}
return false;
}
The call() function attached to Function objects is designed to allow this; calling a function with a desired context. It's an extremely useful trick when setting up event handlers that call back into functions within other objects.
Rob's answer is the best answer for your problem, but I wanted to address something that you wrote in your original question:
I know I can pass this as an argument to handleOnClickConfirmed, but some of my code already uses handleOnClickConfirmed and I don't want to have to rewrite those calls.
JavaScript parameters are always optional, as far as the interpreter is concerned. For example if you have the function:
function MyFunction(paramA, paraB) {
// do nothing
}
All of these calls will execute without error:
MyFunction(1,2);
MyFunction(1);
MyFunction();
So you could modify handleOnClickConfirmed to accept what would essentially be an optional parameter. Like so:
function handleOnClickConfirmed(context) {
context = context || this;
// use context instead of 'this' through the rest of your code
}
Again, in this particular case, the call function is the best solution. But the technique I outlined above is a good one to have in your toolbox.