Lee
Lee

Reputation: 5936

Callbacks in JQuery / JavaScript

I was trying to bind an on click event to a table tr in JQuery. The first example below worked as expected however the second one did not. The populate_form function fired without waiting for the event.

I was wondering if someone could explain to me why?

Thanks in advance..

Example One

.click(function() { populate_form(row.name, row.age, row.dob); });

Example Two

.click(populate_form(row.name, row.age, row.dob));

Upvotes: 1

Views: 235

Answers (4)

J. Holmes
J. Holmes

Reputation: 18546

The quick answer, which will probably already be answered and accepted by the time that I post this answer, is that your second version invokes populate_form and then passes the result of the function to click. Which, in this case, isn't what you want. What .click() is really expecting is a reference to a function to execute, when something triggers that event.

That's the quick answer; which may or may not make sense immediately. So, let's look a
contrived simple example. Consider the following code:

var Foo = function() {
    var noop = function() { };
    this.callback = noop;
}

Foo.prototype.setCallback = function(callback) {
    this.callback = callback;
}

Foo.prototype.go = function() {
    this.callback.apply(this);
}

This is a gross over-simplification of what jQuery is doing, but it gets the point across. We have a little class, and its got two methods: setCallback and go. Now, the idea is that when we call go() we want to execute whatever function we have provided. Also note that default callback is set to a function that is empty (or noop).

So if we do this:

var bar = new Foo();
bar.go()

Then nothing will happen, but we wont get any errors. That's because that empty noop function gets invoked.

Now we have to talk about the two ways to define a function in JavaScript: A function expression and a function declaration.

A function declration looks like this:

function sayHelloDeclaration() {
    alert("Hello from a declaration!");
}

And if we wanted to invoke that function, we would write:

sayHelloDeclaration();

And we would see the alert. However, if we just wanted to reference the function itself (without invoking it), then we would just use its name:

sayHelloDeclaration;

Notice the lack of parentheses; that makes this just a reference to the function. It was never told to do its work, so the alert never appears. Now, let's look back at our Foo class, and more specifically at the setCallback function:

Foo.prototype.setCallback = function(callback) {
    this.callback = callback;
}

Notice that this method accepts one argument, and assigns it to an property on this. Basically, we want to save a reference to a function for later use. And lets look at the go method as well:

Foo.prototype.go = function() {
    this.callback.apply(this);
}

This takes whatever is stored in this.callback and tries to invoke it using, .apply(), we could have also used .call() (subtle difference between those two). Or would have just said this.callback().

So lets, look at how it would be used. If I were to write...

function sayHelloDeclaration() {
    alert("Hello from a declaration!");
}

var bar = new Foo();
bar.setCallback(sayHelloDeclaration);

...you would not see the popup. However, adding the last line...

function sayHelloDeclaration() {
    alert("Hello from a declaration!");
}

var bar = new Foo();
bar.setCallback(sayHelloDeclaration);
bar.go();

... you would see the popup. This is because go(), invoked the reference to sayHello that it had kept track of. However, you second example is doing something like this:

function sayHello() {
    alert("Hello from a declaration!");
}

var bar = new Foo();
bar.setCallback(sayHelloDeclaration());

Notice that you get the popup, however we never called bar.go(). That is because sayHello() is invoking the method. The return value from that function is being passed into setCallback. If we tried to call bar.go(), we would get a type error because we tried to set callback to something that wasn't a function and then tried to invoke it. This is a "bad thing".


Now, what about function expressions? A function expression is a different way to define a function. Kind of the same way you would define a string or a Number in JavaScript. It has a similar but slightly different form:

var sayHelloExpression = function() { alert("Hello from expression"); };

This essentially creates a new anonymous function and assigns it to the variable sayHelloExpression. So we can use it just like out previous example:

var bar = new Foo();
bar.setCallback(sayHelloExpression);
bar.go();

Or we could skip assigning it to a variable all together and just say:

var bar = new Foo();
bar.setCallback(function() { alert("Hello from expression"); });
bar.go();

Now there are some subtle (but important) differences between function declarations and function expressions that other people have gone into in more details about, but the key to understand here is that you can talk about a function, without invoking it. You can also keep track of a reference to a function and then invoke it later.

In your first case you are creating a function expression that is invoking another function with some arguments. Just like our alert in our function expression, except it is a user defined function. But what you are giving setCallback is still a reference to a function that won't be invoked until later.


Lastly, let's look at a reasonable use to invoke a function using when dealing with a callback like this. An example would be when you have a function that returns another function. Consider this function:

function createGreeter(name) {
    return function() {
        alert("Hello there " + name + "!!")
    }
}

This function takes one argument, and then builds and returns another function.

var sayHiBob = createGreeter("Bob");
sayHiBob();  // alerts "Hello there Bob!!"

This function is like a little factory for us to create other functions with. In this case, it makes total sense for use to invoke this function when using it with our setCallback method, because we want to set the callback to the result of the function that we are invoking. Something like this.

var bar = new Foo();
bar.setCallback(createGreeter("Joe"));
bar.go(); // alerts "Hello there Joe!!"

There is plenty more to learn, but that's my long answer to this question.

Upvotes: 4

Diode
Diode

Reputation: 25165

You can't do it like that, because click event will be passed to the function as parameter.

.click(function(event){....});

So even if you give

.click(populate_form);

function populate_form will be invoked with the event as parameter. So the result will be

populate_form(event);

So the right way is your Example One

.click(function(event) { populate_form(row.name, row.age, row.dob); });

Upvotes: 0

JaredPar
JaredPar

Reputation: 755457

In the second version you executed the function directly. Javascript doesn't have any particular understanding of .click and hence treats it just like any other function call. It treats it no differently than

.anyName(populate_form(...))

In both cases it promptly executes the function populate_from and passes the result to the method. In order to establish it as a callback you need to explicitly wrap it in a function whose value is then passed into the method

.anyName(function() { populate_form(...); });

Upvotes: 1

box86rowh
box86rowh

Reputation: 3415

jquery needs either a reference to a function, or the function itself, you cannot pass in straight code. so either:

.click(function(){....});

or

.click(populate_form);

Upvotes: 0

Related Questions