2

for my own personal improvement, I was fiddling with closures and functions in JS, when I found this behaviour that deeply puzzles me.

Take this function, assign it to a variable, and call it from two different HTML elements via jQuery:

var print = function(){
console.log("Hello" );
};    

document.getElementById('element1').onclick = print();
document.getElementById('element1').onclick = print;

Why on earth is the second element, if clicked, to be the one that prints correctly "hello"?
I always thought you need to put brackets after a function's name to call it.

In fact, if I just call the function by itself (and not via a jQuery event) it works as expected:

var print = function(){
console.log("Hello" );
};    
print; //does nothing
print() //prints "Hello"

What I am missing here? Is something related to jQuery? Thanks in advance!

c1c1c1
  • 355
  • 1
  • 3
  • 9
  • You are referencing the same elements. In addition you are setting the onclick to both the reference of the return value of the function (which isn't what you want to do probably) instead of to the function itself. – Sam-Graham Jan 29 '15 at 17:59
  • This might help: http://stackoverflow.com/a/7969111/438992 – Dave Newton Jan 29 '15 at 17:59
  • @Sam-Graham Yes, but that's not really the underlying issue here. – Dave Newton Jan 29 '15 at 18:00
  • thank you @A.Wolff Could you elaborate a bit more? What's wrong with calling the function itself as opposed to a reference? And what do you mean by use "parenthesis?" – c1c1c1 Jan 29 '15 at 18:02
  • Because calling the function *calls* it, and assigns its return value to the `onclick` handler. See my "answer" below, and the link I provided above, for further information. "Parentheses" are the name of the characters used to call a function, not brackets, at least in US English. – Dave Newton Jan 29 '15 at 18:04
  • @c1c1c1 I removed comment because ssube explains it better ;) – A. Wolff Jan 29 '15 at 18:04
  • @DaveNewton thank you Sir. I'm marking it as the correct answer and closing the question. thanks everyone. – c1c1c1 Jan 29 '15 at 18:05
  • 1
    @c1c1c1 ssube's answer is the correct one, and borrows from what I wrote, for a fairly complete explanation. This is actually a dupe question, but I can't search for the dupes easily at the moment. – Dave Newton Jan 29 '15 at 18:06
  • yep sorry, I meant that one, but i initially thought you wrote it. I'm gonna accept @ssube's answer of course. still, thank you both. – c1c1c1 Jan 29 '15 at 18:08
  • Definitely not related to jQuery, considering that there doesn't seem to be any jQuery in your code above... ;) http://jsfiddle.net/5L1cyhmL/ – Josh Taylor Jan 29 '15 at 18:22

5 Answers5

8

The difference is calling a function vs taking a reference to a function.

The syntax func() immediately calls the function provided. In element.onclick = func(), the event will be bound to the return value of func.

With element.onclick = func, you are not calling func, simply referencing it and assigning it to the event.

If func happened to return a function, then you could use the element.onclick = func() syntax, and it would do what you expect.

document.getElementById('element1').onclick = print(); // Assigns the *return value* of print
document.getElementById('element1').onclick = print; // Assigns print

function returnPrinter() {
    return function print() {
        // do stuff
    }
}

document.getElementById('element1').onclick = returnPrinter(); // Assigns the return value, which is a function, thus doing what you want
ssube
  • 41,733
  • 6
  • 90
  • 131
0

(Too long for comment.)

If you really want to mess with your own head:

var print = function() {
  return function() {
    console.log("Hello");
  };
};    

document.getElementById('element1').onclick = print();

This will do what you expect, but perhaps not why you expect it to.

Dave Newton
  • 152,765
  • 23
  • 240
  • 286
0

Using

document.getElementById('element1').onclick = print();

is equivalent to the value returned from print (but there is no returned value, so it is undefined)

document.getElementById('element1').onclick = undefined;

When the onclick event handler is called, it calls the assigned value expecting a function. undefined() will also give you the same error. print() will do what you were expecting, and that is why when you use this it works

document.getElementById('element1').onclick = print;
Travis J
  • 77,009
  • 39
  • 185
  • 250
0

A function is just like any other variable, except it has an additional operator:

Just like with objects and arrays, you can use the dot and square-brace operators to retrieve their properties, you can use the call operator to call the function, but that function is still a variable nevertheless.

function a() {
    console.log("potato");
}
var b = a;
b() //prints "potato" on the console

On your example, using the print function on its own (without the call operator did nothing, but that would be the same as if you wrote a number on its own: the result is the value itself!

console.log(a); //prints function a()
console.log(42); //prints 42

When you type a something on its own line, it's just going to return that value to the expression, but without anything to do, nothing happens:

a; //nothing happens
42; //nothing happens

Now, when you use the call operator, it runs your function and the expression's value is whatever the function returned, or undefined if it didn't return anything.

function doStuff() {
    return "potato";
}

Now suppose you have this expression:

console.log(doStuff());

It will run doStuff, and return to the expression:

console.log("potato");

Then it will run console.log, with "potato" as its parameter, and it will do its magic to show on the console.

Events are just like that, saving a function to a variable to use later:

function doom() {
    return Infinity / 0;
}

onDoomsday = doom;

//somewhere else in your code:

onDoomsday(); //destroys the world
Kroltan
  • 4,424
  • 4
  • 31
  • 52
  • LOL nice final example. I've already accepted the answer, nonetheless, thank you for clear and detailed explanation. – c1c1c1 Jan 29 '15 at 21:51
0

you have to bind reference of a function to onclick but in first case you actually doing nothing other than calling a function:

document.getElementById('element1').onclick = print();

It will actually invoke the function print() and since function has nothing in return so it will not impact on element1 click event.

on the other hand document.getElementById('element1').onclick = print;

In order to make first case working you need to return a function so it can be invoked on event:

var print = function() {
  // when this will invoke it will return function
  return function() {
    console.log("Hello");
  };
};    

will assign reference of function to onclick event and it invoke the function on each click event.

Zaheer Ahmed
  • 26,435
  • 11
  • 70
  • 105
  • If the event is already bound, `onclick = print()` actually could have an effect: it will unbind the event from the previous handler, as it binds to `undefined`. – ssube Jan 29 '15 at 18:15