Difference between 2 jquery binds - javascript

function runSomething () {
// some stuff happens
}
$(selector).bind('event', runSomething());
$(selector).bind('event', runSomething);
What's the difference between these 2 versions of the bind?
Here's a practical example:
http://jsbin.com/icajo/edit
Can somebody explain why works the way it does.
I'm trying to get multiple buttons to run the function upon the event, what should I do?

In first case you bind result of runSomething() call, in second - function itself.
update
#JSNewbie, run this and tell what you see in each alert.
function runSomething () {
return 3;
}
var a1 = runSomething();
var a2 = runSomething;
alert(a1);
alert(a2);

In javascript, passing a set of parameters to a function invokes the function, it gets evaluated to the functions return value.
var test = function() { return 1; } // Creates a new function that returns 1
alert(test.toString()); // => "function () { return 1; }"
alert(test().toString()); // => "1"
Even alert itself is just a variable that points to a function.
alert(alert); // => "function alert() { [native code] }"
So, if the first example, when calling runSomething(), it immediately evaluates that function, then passes the return value as a parameter to bind(). In your case, it evals the alert() as the page is loaded, then passes undefined to bind()
In your second example, using the variable runSomething the function itself is passed to bind(). Bind then uses that function only when the event has been raised.
To really blow your mind, you could have function that returns a function, then evaluating the function (like in your first example) is correct... For example
var counter = 0;
function GenerateNext() {
counter++;
return new Function("alert(" + counter + ")");
}
a = GenerateNext();
b = GenerateNext();
b() // will alert 2
a() // will alert 1
$(selector).bind('event', GenerateNext()); // alert 3
$(selector).bind('event', a); // alert 1
$(selector).bind('event', b); // alert 2
It just all depends on what you are trying to do; pass the function itself, or pass the return value of the function.

In the first line the function runSomething is executed within the bind statement, and that what it returns is bound to the event, for example if your runSomething function returns another function then that function is bound, and will be executed upon the event.
On the second line the runSomething function is not executed at that line, and is only only execute when "event" happens.

In javascript functions are treated as variables. Adding the "()" will call the function, and pass the result of the function (which could be 'undefined' if the function returns nothing). The second is the proper way of using the bind method, because it gives a handle to the function to call when the event is triggered.

$(selector).bind('event', 'runSomething()'); (notice the extra quotes around 'runSomething()')
run the function runSomething() when the event is received.
$(selector).bind('event', runSomething);
sets the function runSomething() as the callback function for the event, which means it will receive whatever parameters are included in the event, often this is useful for currentTarget (so you can use the same event on many buttons) or to get specific information from the event (mousemove returns the X,Y location of the mouse as it is triggered).
So if you needed to access the event object that is returned when the event is triggered, the first version wouldn't work properly.
function runSomething(event){
console.log(event); // this would show the event object if the second code is used.
}

Related

Attempting to pass a string to a constructed function

still floundering around in my attempts to understand functions. How would I go about constructing a function with values I want to pass to it?
var box = $('#box1');
function pushCard(arg1) {
if (this.style.opacity == 0.5) {
this.style.opacity = 1;
}
else {
this.style.opacity = 0.5;
window.alert(arg1);
}
}
box.click(pushCard('String'));
tl;dr: be aware of the difference between functions / function results and when functions are passed as values / when they're called (and their result is passed)
The culprit is with this line:
box.click(pushCard('String'));
You're calling box.click() with "something". JavaScript needs to evaluate expressions before passing them as function arguments.
In this case, you instruct JavaScrip to run box.click(pushCard('String')) = call box.click with value of pushCard('String') as first parameter.
In order to do this, JavaScript first needs to evaluate pushCard('String') by running pushCard with value 'String' as first parameter (this doesn't need more evaluation since it's already a value).
The result of pushCard('String') is undefined (you're returning nothing from that function). So in effect, it's the equivalent of box.click(undefined).
This is what you want:
box.click(function() {pushCard('String')});
(or with ES6 arrow functions: box.click(() => pushCard('String'));)
In this case, you're assigning box.click() a function. This is what jQuery .click()` expects, a click handler which will run (be evaluated) then the click happens, not when the click handler is assigned.
In JavaScript, you can pass functions as values, and they are evaluated when explicitly called:
function test() {
alert('I have been called');
}
function delay_it(handler) {
setTimeout(function() {
handler(); // ⇽ call given function
}, 1000);
// shorter: setTimeout(handler, 1000);
}
// Wrong (we're calling test() by ourselves and passing the result (undefined) to delay_it
delay_it(test());
// Correct (we're giving test as function to delay_it, delay_it will call the function)
delay_it(test);

Event listeners with arguments

I want to pass arguments to an event listener in Javascript.
I have found solutions however I cannot understand why or how they work and why other solutions do not work.
I have a C/C++ background, however in Javascript functions perform a lot different.
Could you please help me understand how and why the following examples work or not work?
Without parameters the code works fine:
var clicker = document.getElementById("mainNavToggle");
clicker.addEventListener("click", eventFunction);
function eventFunction()
{
console.log("works");
}
If I include brackets on the eventListener the function executes once without the event firing and then does nothing:
var clicker = document.getElementById("mainNavToggle");
clicker.addEventListener("click", eventFunction());
function eventFunction()
{
console.log("works");
}
I suppose this is because the function is invoked at the eventListener and this doesn't allow the eventListener function to call the eventFunction later.
When I want to pass an argument with the eventFunction.
The following does not work:
var clicker = document.getElementById("mainNavToggle");
clicker.addEventListener("click", eventFunction("works"));
function eventFunction(myStr)
{
console.log(myStr);
}
It invokes the eventFunction without the event firing and then when the events fires does nothing which is the same behaviour as previously.
So far I think I can understand.
To pass the argument correctly in the event function I found the following code.
The following solution works fine:
var clicker = document.getElementById("mainNavToggle");
clicker.addEventListener("click", eventFunction("works"));
function eventFunction(myStr)
{
function func(event, myStr)
{
console.log(myStr);
}
return function(event) {
func(event,myStr)
};
}
The first thing I notice is that this eventFunction is not invoked immediately.
Why is that?
However, I am at a loss of how arguments are passed within each function.
I understand that the "works" literal is passed to myStr on the eventFunction.
However in the function func there is an additional event parameter not passed by eventFunction.
Also why does the eventFunction needs to return another function with one parameter that internally calls the func function?
Also please notice that I want to pass parameters to the eventFunction that do not have a global scope, otherwise there would not be a need to pass parameters.
Let's dissect this step-by-step.
General information
The .addEventListener function/method takes in 2 (or 3) arguments:
a String, with a value of the event type/name (for example "click")
a pointer to a Function, which should be executed when the event occurs
a Boolean, specifying if event bubbling or event propagation should be used. This argument is optional and can be omitted.
Example 1
In you first example, you pass a String ("click") and a pointer to a Function (eventFunction) into .addEventListener. Everything works as expected, the event occurs and the function is executed.
Example 2
In this example, you pass a String and undefined to .addEventListener, which is not what .addEventListener expects. You might ask yourself "When did I pass in undefined?". Well, it happened as soon as you executed your eventFunction by adding parens () after it. Here, the syntax in JS is equal to the syntax in most other languages, adding parens to the right side of a function executes the function.
Because your eventFunction doesn't return anything explicitly, it automatically returns undefined. Therefor, this is what .addEventListener got:
clicker.addEventListener("click", undefined)
However, because you executed eventFunction, it logs "It worked" to the console once.
Example 3
This example fails for the same reasons example 2 failed.
Example 4
The code in the last example works, because it uses a concept called closure. There are literally thousands of explanations of the concept, so my explanation here will be really short. If you have trouble understanding it, just google for
"javascript closures".
In contrast to a lot of other languages, JavaScript has function scoping instead of block scoping. So if you define a variable inside a function F and return another function G from within F, G will have access to variables and arguments from inside F. For demonstration purposes:
function F () {
var fVariable = 'Defined in F';
return function G () {
return fVariable; // <-- this is the closure
}
}
var funcG = F(); // <-- execute F
funcG(); // -> 'Defined in F';
Compare this short example with your fourth code: They are pretty much the same. The only difference is, that your code creates an extra function func.
Now, if you call clicker.addEventListener("click", eventFunction("it works")),
you execute eventFunction, which returns another (anonymous/lambda) function which encapsulates the "it works" string via a closure. If we write it out by hand, this is what .addEventListener "sees":
clicker.addEventListener("click", function (event) {
func("it works", event);
});
EDIT: Solving the problem
Here's a working example, note that I changed the function names to reflect their purpose:
function eventHandlerFactory (myStr) { // <-- eventFunction in your code
return function executeOnEvent (event) { // <-- executed if an event occurs
console.log(myStr);
console.log(event);
}
}
clicker.addEventListener("click", eventHandlerFactory("it worked"));
The eventHandlerFactory function returns a function executeOnEvent, which is passed into .addEventListener. Writing it out by hand again, this is what the interpreter understands:
clicker.addEventListener("click", function executeOnEvent (event) {
console.log("it works");
console.log(event);
});
The first thing i notice is that this eventFunction is not invoked
immediately
It is invoked immediately. However, it returns a new function. This is the function that is registered for the click handler. This function, when called, calls the function func in a closure. The arguments passed to this function are also preserved in a closure.
To understand this concept, you need to understand how closures work. This is a topic that has been written about extensively, so I'll just point you to How do JavaScript closures work?.

Why function returns unexecuted

If I write this code:
var foo = function (){
var x = 5;
return (function (){ return x;})();
}
alert(foo());
it alerts 5, as intended. But, if I do not use IIFE:
var foo = function (){
var x = 5;
return function (){ return x;};
}
alert(foo());
it alerts
function(){return x;}
I understand that in the first example IIFE runs and whatever it computes gets returned.BUT, if not using IIFE function returns without being executed.
Why is function returned before it is executed?
Functions in JS are first class objects. You can treat them in the same way as you can treat any object, including pass them about between functions.
If you don't do something to call a function, then it doesn't get called.
So is function(){return x:} which gets alerted in the second option a string
It is a function.
Functions, like any object, have a toString method.
alert() expects to be passed a string. Since you pass it an object, it will get converted to a string, which is done by calling toString().
Basically you are not executing the function before returning anything in the second example. You are just returning a function definiton with this row: return function (){ return x;};. This is why the function returns just the function that you placed after the return.
Functions are never called automatically. They can be passed as arguments, returned as values, assigned to variables, etc. and this just moves around references to the function. In order to call a function, you have to use the () operator.
Your first example does this by calling the function in the IIFE. "II" stands for "Immediately Invoked", and refers to putting () right after the anonymous function expression, so that you define it and then immediately invoke it.
In your second example, you simply return the function, and then pass it as an argument to alert(). alert() has no reason to call the function (it's not like map() or forEach(), which take a callback argument and are specified to call the function). If you want to execute the function, you need to call it explicitly, e.g.
var foo = function() {
var x = 5;
return function() {
return x;
};
}
alert(foo()());
The double () means to first call foo, then call whatever it returns, and finally pass the result to alert.

Why can't the generators next function be a callback function for an event listener [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 7 years ago.
I was experimenting with generators for a bit. When I tried this piece of code I got unexpected errors:
Uncaught TypeError: Method [Generator].prototype.next called on incompatible receiver #
or
TypeError: CallStarGeneratorMethodIfWrapped method called on incompatible HTMLButtonElement
My question, does it not work ? What is the meaning behind those error messages ? And most importantly; Why is first().next not handled as a normal function ? Why does the addEventListener care about the origins of the first().next function. Type first().next in the console. It says function. Below is out commented a similar function to the next except it produces always the same result.
The code, that you can try to reproduce:
<html>
<button id="test">Click</button>
<script>
var first = function* (){
console.log("first click");
yield true;
console.log("second click");
yield true;
};
document.getElementById("test").addEventListener('click', first().next);
/*var wouldwork = function (){
console.log("would work");
return { value: true, done: false };
// same as the next returns
};*/
/*document.getElementById("test").addEventListener('click', wouldwork);*/
</script>
</html>
Another option would be to put next with the correct context in another function. To do that we store the iterator in a variable.
var iterator = first();
document.getElementById("test").addEventListener('click',
function (event){return iterator.next();}
//context of next is always the correct one
);
If that happens more often it can be a good idea to create a new function named createNext that returns a next function in a more pure functional style
var createNext = function (generatorFunction){
var iterator = generatorFunction();
return function() {
return iterator.next();
};
};
document.getElementById("test").addEventListener('click',
createNext(first)
);
jsFiddle Demo
The way that event listen works is that it will call the function handle using call and assign the this binding from its execution context to the called function handle. So this is going to be bound as the context for next being called. That is why it doesn't work as written.
Next, there is the issue of actually getting the function from your generator function for iteration. "Calling a generator function does not execute its body immediately; an iterator object for the function is returned instead.". This means that yes, first().next is a function object, but it isn't the handle you want to pass. You simply want to use the iterator function itself, but that would be first(), so how does that work if you want it to call next each time?
An option here is simply to take your generator function and pass the iterator function in later as a binding. In order to maintain the iterator function's binding inside of .next, you could do this:
document.getElementById("test").addEventListener('click', first().next.bind(first()));
This will bind the iterator function for first() to the iterator function's .next function. Kind of messy. The first call to first() exposes the iterator function and then accesses its next function, the second call simply binds the next function's this to the iterator function which is otherwise overwritten when the eventListener overrides the this binding using call.
You can read more in general about these generator functions and their iterators here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*

Function call with parameters to addEventListener inside a loop [duplicate]

This question already has answers here:
How to pass arguments to addEventListener listener function?
(36 answers)
Closed 7 years ago.
Problem : I would like to call a function "test " with passing parameters using the click event of the button.
using addEventListener ('click', test ); works.
using addEventListener ('click', test(parameter)); the function is activated automatically when loading and not to click on the button .
Question: how to pass parameters ?
//create 3 button (n is a counter):
var btn = document.createElement("INPUT");
btn.setAttribute("id", "demo"+n);
btn.setAttribute("type", "button");
btn.setAttribute("value", ("click"));
document.body.appendChild(btn);
call Without parameters works correcty:
function test(m) {
alert("YOU CLICKED ME!");
}
var m=0;
while(m!=3){
document.getElementById("demo"+m).addEventListener("click", test);
m=m+1;
}
`
with parameters not works (the function is activated immediately):
function test(m) {
alert("YOU CLICKED ME!"+m);
}
var m=0;
while(m!=3){
document.getElementById("demo"+m).addEventListener("click", test(m));
m=m+1;
}
Wrap your function in another function to avoid invoking it immediately
document.getElementById("demo"+m).addEventListener("click",(function(x){
return function(){
test(x);
}
})(m));
In your case addEventListener expects a function object so you can't use parenthesis in front of function as it invokes the function and passes the returning value. So you need to return the function object which invokes test function( using an anonymous function to avoid closure property of javascript functions)
EDIT: Reason for returning wrapper function of test using an anonymous function
We can't pass
function(){
test(m);
}
to addEventListener directly.
As every function object in javascript remembers the environment( in our case variable m) outside their lexical scope . If you just create different function(like in while loop) in same environment( variable m ) then they will reference to same environment.
You have two use a function factory to avoid creating closures in loop like described above.
Explanation about why addEventListener('click', test(parameter)) doesn't work
The addEventListener function can accept two parameters, a event type and a listener object that receives the event. In most cases, the listener object is simply a JavaScript function.
So when you use
addEventListener('click', test);
You are passing the string click as the event type, and a function test as the listener, which is what you have expected.
But when you use
addEventListener('click', test(parameter));
The listener is actually the value that the expression test(parameter) evaluates to. Since test(parameter) is a function call, you actually pass in what what is returned by test(parameter), in your case, 'undefined' since test function does not return anything. This is why test is called immediately - it need to be invoked to get the return value as a parameter of your addEventListener function call.
So to execute a function with custom parameters when the event happens, as others have suggested, you can use the anonymous function approach.
Try this:
function test(m) {
alert("YOU CLICKED ME!"+m);
}
var m=0;
while (m!=3) {
(function(m) {
document.getElementById("demo"+m).addEventListener("click", function() {
test(m);
});
})(m);
m=m+1;
}
this will create new scope for variable inside to loop.
You need to build versions of the test function, using what is called a "high-order function":
// this is a high order function, since it returns a function
function buildTestFunction(m) {
return function () {
alert("YOU CLICKED ME!"+m);
};
}
var m=0;
while(m!=3){
var test = buildTestFunction(m);
document.getElementById("demo"+m).addEventListener("click", test);
m=m+1;
}
PS. You might want to use a for loop here, for clarity.
for (var m = 0; m < 3; m++) { ... }

Categories

Resources