I'm trying to create a bunch of buttons via loop, and use the loop iterator as an argument for each of the button onclick functions.
My code:
var testFnc = function(i) { alert("Arg: " + i); }
mainDiv.append(create_button(name, "buttonCSS", testFnc(i)));
However the functions are called automatically as the page loads and the buttons are placed (i.e. I see the alerts right away).
I'm sure there's some common design pattern for this.
Thanks!
One approach is to, for each button, call a "self-executing" function that creates a separate function.
mainDiv.append(create_button(name, "buttonCSS", (function(i) {
// For the function below, i comes from
// this function's scope, not the outside scope.
return function() {
testFnc(i);
};
})(i) ));
This will allow you to change the value of i outside the function and leave existing buttons unaffected.
(If you are creating a lot of buttons (perhaps thousands), it might be better to change the create_button function to add a property to the button element itself and have your function check that. Or if your code does not need to work in Internet Explorer 8 or below, you can use the bind() function instead of the above code.)
Note: for a very detailed explanation of this, please see my previous answer to an identical question.
By using parentheses, you're calling testFnc and passing its return value to the create_button function, so of course it alerts right away. Instead, you need to pass in an actual function object without invoking it. Do this by wrapping testFnc in an anonymous function, ala function() { testFunc(i); }. Can you see how this returns a function that will be run later rather than immediately?
This by itself still won't work, since i participates in a closure around it, so that when the click event runs it uses the most recent value of i (the value it was at the end of the loop), rather than the one at the time of binding. To fix this, create a new closure around a different variable—positioned only in the immediate scope of your callback and no higher—that will be given the value of i so that the click event has the value as of the time of binding, not the time of execution of the bound function.
Here's how to do this:
for (i = 0; i < len; i++) {
mainDiv.append(
create_button(name, "buttonCSS", (function(val) {
return function() {
testFnc(val);
};
}(i)))
);
}
I'm sure that looks a bit confusing, but the point is that wrapping the function that is called (the one that is returned) in another function creates a new scope with a variable val that isn't used in any outer function. This makes the closure only over the immediate value, detaching it from i, which is what you want. There is a deeper explanation of this here.
Please note that one other answer on this page reuses i which could be confusing. That will work, but then it is unclear inside of the inner function which i is being referred to! I think it is better practice to avoid any confusion by using a different variable name.
Are you using jQuery? You could reference the class of a button
<button class='my-custom-class' data-index='0'>This button</button>
and in document.ready:
$(document).ready(function () {
buildButtons();
$('.my-custom-class').on('click', function () {
var index = $(this).attr('data-index');
alert('Arg: ' + index);
});
});
I suggest using a custom data-* attribute since you can use the attr property in jQuery to access the custom attribute. This is one way you could implement it.
Related
I'm working on a project right now which is primarily front end UI.
Right now i'm encountering an issue with one of my functions, in which the return value is returning the function itself and not the production of the function.
I'll first start by explaining the expected results:
1.) There is a menu on the page, when clicking on a button in the menu another menu is added to the page with it's own buttons - i'll refer to this as the sub menu.
2.) When clicking the button available in the sub menu, another menu is create and added to a list elsewhere on the page.
I'll explained the code that I have, as well as where the issue lies below:
1.) Here is where the menu button is created:
var add = document.createElement('button');
2.) Here is where I add the event listener to the button above:
closure = (function(arrayItem)
{
var test = function(event)
{
object.spaces.push(selectedSpace.id);
object.spaceobjects.push(selectedSpace);
var enemyPanel = addEntityPanel(roomnumber, selectedSpace.id, object, object.savelocation, true);
return enemyPanel ;
};
return test;
})(arrayItem);
add.addEventListener("click", closure, false);
I used the variable closure and passed it an anonymous function for closure reasons, this way the variable arrayItem can be modified through a for loop.
You may notice the function addEntityPanel, this function creates the menu that I've been referring to and adds it to the list, it also returns an object with all of the html elements used to create the menu - storing it in the variable enemyPanel.
enemyPanel from the test function is returned to the variable test and test is returned to the variable closure which is supposed to give me access to the object enemyPanel created in the addEntityPanel function; however all that is being returned is the function itself as a string. I've tried to change:
return test;
to
return test();
But all this does is execute the function when the button in the sub menu is created. I only want it to be executed when the button is clicked.
I'm not too familiar with closure, so if there's something wrong with the closure function preventing it from generating the desired output - I would not be surprised.
I'm open to any and all suggestions. Please let me know what you guys think, thank you!
...enemyPanel from the test function is returned to the variable test...
No, it isn't. You're assigning the function itself to the variable test, not the result of calling it.
There are two issues:
You're passing the wrong function to addEventListener. It should be test, the function created by closure, not closure.
add.addEventListener("click", test, false);
You're returning enemyPanel from the event handler. The return value of an event handler added via addEventListener is completely ignored.¹ You can't return enemyPanel to anything. You can create it, assign it to a variable that's in scope for that function, add it to a list, etc., but you can't return it anywhere from within the event handler.
¹ That's a post on my anemic little blog.
I am learning JavaScript and becoming confused by the logic of the code examples. From codecademy. Why are there function set-ups in function calls?
I'm quite confused. I am moving from a simplified C-like langue.
The JavaScript example
var main = function(){
$('.article').click(function(){
$('.description').hide();
$(this).children('.description').show();
});
};
My understanding:
- main is a function name with a return type of var.
$('.article') is a element/object/or class object.
.click() is a call to a member function
But:
???:
.click(function(){
$('.description').hide();
$(this).children('.description').show();
});
This seems to be a newly on the spot created function to run When/If click() is activated or run.
The way I used to think is like this:
var *p_obj = $('.article');
var *p_obj = $('.description');
var do_click()
{
p_obj2.hide();
p_obj.children(p_obj2).show();
}
var main(){
p_obj.click(do_click);
}
Function main() looks at p_obj and calls click().
Click() evaluates to true/false and run the pointer_to function do_click().
Function do_click() looks at the p_obj2 and calls hide(), which performs an action of hiding the p_obj2.
Function do_click() also looks at p_obj and uses children to scope focus to p_obj2, then it runs show(), which preforms an action of displaying p_obj2.
I do realize my C-like example is wrong and odd. I realize my terminology is wrong or otherwise used incorrectly.
The way this design looks seems like I must write extended functionality on-the-spot for every call to .click(), so if-then .click() is run on 3 different items, I'm creating different extended functionality for each object. But I would normally create a single function that varies it's internal execution based on the object or condition click() calls it by.
This set-up seems alright if the code a relatively simple or short, but on-the-spot functional seems like overworking for longer code and code where the functionality repeats but the objects change.
Am I thinking about JavaScript functions with-in functions correctly and is this a design goal of the langue to add long repeating extended functions with-in functions?
Here, you should understand 2 things:
passing functions as arguments
anonymous functions
The first concept is particulary important because callbacks are popular in JavaScript, so let me explain it for callbacks. Imagine we have 2 functions getStuffFromWeb and processStuff. You probably expect that they are used like this:
var result = getStuffFromWeb();
processStuff(result);
But the issue here is waiting for getStuffFromWeb may take some time (the server is busy), so instead they are usually used in a "when you finish, call this function" manner, which is:
var getStuffFromWeb = function(params,callback) {
...
callback(result);
};
getStuffFromWeb(someParams,processStuff);
Well, in fact the structure of getStuffFromWeb will be different, most likely something like this:
var getStuffFromWeb = function(params,callback) {
requestObject.make_request(params)
.onSuccess(callback);
};
So when getStuffFromWeb is called, it starts to listen to response while the code after getStuffFromWeb(someParams,processStuff); goes on evaluating. When the response comes, it calls the callback function to process the data further using the procedure we have defined (processStuff).
The second concept is rather simple: you may of'course write smth like
var processStuff = function() {...};
var getStuffFromWeb = function(params,callback) {
requestObject.make_request(params)
.onSuccess(callback);
};
getStuffFromWeb(someParams,processStuff);
but if you use processStuff only once, why define a named function? Instead, you can just put the very same expression inside the onSuccess param like this:
var getStuffFromWeb = function(params) {
requestObject.make_request(params)
.onSuccess(function() {...});
};
getStuffFromWeb(someParams);
This looks exactly like if we took the value of processStuff and put it directly to the onSuccess's argument (and that's called anonymous function). And also we got rid of an extra argument of getStuffFromWeb.
So basically that's it.
Simple answer is that the second argument of click() requires a callback function.
This can be a named function passed as reference as in your p_obj.click(do_click); example or it can be an anonymous function with self contained logic. Anonymous functions are very common in javascript
It's the same thing just with 2 different ways of declaring the callback.
Note that the only time you would return anything from an event handler function would be to return false which effectively prevents the default browser event (url opening from href or form submit for examples) and stops event propagating up the DOM tree
main is a function name with a return type of var.
No. main is a variable which is assigned an anonymous function. The function name would go between the keyword function and the () containing the argument list.
It has no return statement so it returns undefined.
$('.article') is a element/object/or class object.
It is a call to the function $ with one argument. The return value is a jQuery object.
.click() is a call to a member function
Pretty much. In JavaScript we call any function that is the value of a property of an object as method.
This seems to be a newly on the spot created function
function () { } is a function expression. It creates a function, exactly like the one used to assign a value to main earlier. This question is worth reading for more on the subject.
When/If click() is activated or run.
The click function is called immediately. The new function is passed as an argument.
The purpose of the click function is to bind a click event handler so that when a click event hits the element later on, it will trigger the function passed as an argument.
I do realize my c -like example is wrong and odd. I realize my terminology is wrong or otherwise used incorrectly.
Leaving aside vagaries of syntax. The main difference here is that the click event handler function is that the event handler function is stored in an intermediary variable.
You can do that in JavaScript just as easily, and then reuse the function elsewhere in the code.
var main = function(){
function show_specific_description() {
$('.description').hide();
$(this).children('.description').show();
}
$('.article').click(show_specific_description);
show_specific_description.call($(".article").last()[0]);
};
main();
is this a design goal of the langue to add long repeating extended functions with-in functions?
No. Passing a function expression as an argument is a convenient way to be more concise when you don't want to reuse the function. It's not the only way to pass functions about.
main is currently a function.
It is possible to be overwritten (even to a different type). var is not the return type, it's a statement that main is a variable.
All values should be declared as variables, within the highest scope you intend them to be used (in JS, scope typically means functions, not blocks).
You have the right idea, suspecting that the function gets passed in, and called at a later point in time (and this is actually one of the harder parts for people to get, coming from certain other languages). You'll see this behaviour all through JS.
One key thing to keep in mind in this language (you haven't hit it yet, but you will) is that JS is lexically scoped.
function getInnerX () {
var x = 5;
function getX () {
return x;
};
return getX;
}
var x = 10;
var getX = getInnerX();
console.log(getX()); // 5
The function getX inside of getInnerX has access to the references around it, at the point where it's defined (not where it's called), and thus has live access to the inner x, even if its value changes over time.
This will be another important piece of understanding what you see going on in the language, especially in the case of callbacks.
Okay so what I'm trying to do is when a button is clicked function triggers that creates the following timeout:
setTimeout("if (document.getElementById(lightnum).style.backgroundColor=='green'){document.getElementById(lightnum).dataset.dead=1;document.getElementById(lightnum).style.backgroundColor='red';}", 3000);
The problem I'm having is that because the variable lightnum is reused instantly it makes it so when this timeout triggers it references the current value of lightnum not the value of lightnum when the settimeout was created. The functionality I'm looking for here is when the setTimeout triggers after 3 seconds it references the value of lightnum when it was originally created.
http://jsfiddle.net/657q2/1/
First of all, that code should be in a proper function instead of a string.
Regarding your problem, it's fixed like this:
var callback = (function(target) {
return function() {
if (target.style.backgroundColor == 'green') {
target.dataset.dead = 1;
target.style.backgroundColor = 'red';
}
};
})(document.getElementById(lightnum));
setTimeout(callback, 3000);
The problem with your original code is that lightnum in your original callback evaluates to whatever its value is when the callback is invoked, as you have already seen. What you want instead is to somehow "freeze" it to its initial value.
One attempt to do that would be to make a local copy inside the function that sets the timeout (var copy = lightnum; etc). However, this will still not work because this time when the callback is invoked it will operate on the last value that copy had the last time this function was called (possibly, but not necessarily, the same behavior as your original code).
What you really want to do is put the current value of lightnum somewhere that is only accessible by the code of the callback; in JS, the only way to do that is pass it to a function as an argument. This necessitates the funky "function that returns function" syntax above: the inner function is the actual desired callback and the outer function is a "firewall" that prevents any outside meddling with the variable in question.
Use a closure instead of a string:
setTimeout(
(function(ln) {
return function() {
if (document.getElementById(ln).style.backgroundColor=='green') {
document.getElementById(ln).dataset.dead=1;
document.getElementById(ln).style.backgroundColor='red';
}
};
}(lightnum)),
3000
);
"Jon" and "Ted Hopp" have proper answers, but I might just add why functions are better here:
As Barmar stated, a string evaluation will be in global scope.
IDEs will not syntax highlight within strings, so it makes your code less readable to yourself and others.
It is slower to evaluate.
Most seriously, if a portion of your string is from untrusted input, it could cause a security problem (e.g., if part of the string is coming from your database of user comments, a malicious user could add code there to grab the user's cookies).
I am a relatively experienced c# (and before that c++ Win32) developer, I am new to javascript and have a question regarding the this pointer.
I am using knockout.js, and one function called subscribe accepts a this variable, that will be set inside the callback function.
From my way of thinking from the Win32 days and C#, on any callback function i want a scope object which contains my state.
In this case I have use the this javascript thing to set my callback scope.
My questions are:
Now everything works (full fiddle here if you are
interested), but have I done something terrible?
Is there any reason this is used instead of passing in an explicit
scope variable as a parameter (that would make things easier to understand as
for me, this makes the workings kind of hidden).
What is the intended use for this?
From http://knockoutjs.com/documentation/observables.html it says:
The subscribe function accepts three parameters: callback is the function that is called whenever the notification happens, target (optional) defines the value of this in the callback function, and event (optional; default is "change") is the name of the event to receive notification for. Example below
myViewModel.personName.subscribe(function(oldValue) {
alert("The person's previous name is " + oldValue);
}, null, "beforeChange");
My code snippet below:
var computedOptions = createComputedDepdency(viewModel[option.requires.target],option.data);
viewModel[option.optionsName] = computedOptions;
console.log("making callback scope object for: " + option.optionsName );
var callbackScope = {
callbackName: option.optionsName,
options: computedOptions,
selectedValue: viewModel[option.selectedName]
};
// when the list of available options changes, set the selected property to the first option
computedOptions.subscribe(function () {
var scope = this;
console.log("my object: %o", scope);
scope.selectedValue(scope.options()[0].sku);
console.log("in subscribe function for..." + scope.callbackName);
},callbackScope);
First a semantic note:
The scope of a function is not related to this word. The context is related to this. The scope is related to the accessibility of variables and functions inside another function.
When you try to read a variable outside the function where it's declared, then you trying to access to a var outside its scope. So you cannot do it because the var is inside a scope not accessible from current position.
Now everything works (full fiddle here if you are interested), but have I done something terrible?
If it works, it's not so terrible :-)
Is there any reason this is used instead of passing in an explicit scope variable as a parameter (that would make things easier to understand as for me, this makes the workings kind of hidden).
a fast read: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
In javascript the value of this is determined by how a function is called.
In one way this approach could save annoying passage of context as argument: in a well documented library, the this use is very intituitive.
In other cases, I agree when you change continually context in your app without a rigorous logic, it could be confused.
What is the intended use for this?
We should always remember how and when the javascript is born. It was born for browser in order to interact with the DOM.
For this purpose, the context has sense that change based of which element call the function.
For example:
var divs = document.getElementsByTagName('DIV');
for(var i = 0; i < divs.length; i++) {
divs[i].addEventListener('click',_clickHandler);
}
function _clickHandler() {
this.innerHTML = "clicked";
}
DEMO http://jsfiddle.net/AYBsL/1/
This is an example to how is useful the implicit change of context in javascript.
You could do this also for user-defined function: when you call a function you could change the context:
_clickHandler.call(divs[0]); // simulate click of first div
In javascript 'this' refers to the object that called your function. Only in a situation when you use 'new' keyword you can expect it to point to the current object (function).
var MyObject = function () {
this.hello = function () { console.log(this); }
}
var instance = new MyObject();
There is a way to make sure that this is always what you expect and that is creating a variable to store the correct reference for you and use that instead of this... in your example it would be similar to this...
computedOptions = function () {
var that = this;
}
computedOptions.subscribe(function () {
console.log("my object: %o", scope);
scope.selectedValue(that.options()[0].sku);
console.log("in subscribe function for..." + that.callbackName);
},callbackScope);
MDN JavaScript reference would inevitably explaing it more better then myself, have a look at it.
You shouldn't mix scope and this. this is supposed to mimic classical-oop languages like java or++, that is to keep the reference to an instance object. But it can be used just to execute arbitrary function on a given context using .apply() or .call.
What about scope, you don't have to do anything to pass the scope to a function, since the outer scope becomes automatically accessible inside function. You should read about closures - it's the best part of javascript.
I have a situation like this one:
talenti = $(".talenti");
filtra = $(".filtra");
wrapNavHeight = $("#wrapNav").outerHeight(true);
filtra.click(function(e) {
e.preventDefault();
$(".nasco").hide();
$("#sliding-navigation").delay(delay).show();
delay += 500;
talenti.removeClass('opened');
filtra.addClass('opened');
filtra.attr('id',"focF");
talenti.attr('id',"");
if (filtra.hasClass("opened")) {
$("#wrapNav").slideToggle("100", "linear", function(){
alert(wrapNavHeight);
$("#container").animate({"height": "+=wrapNavHeight"}, 100,function(){
$(".box").animate({"top": "+=wrapNavHeight"});
});
});
}
});
I am trying to get wrapNavHeight but alert(wrapNavHeight); outputs null; can't then assign that value to the next animate lines
Anyone?
isn't it just that you are assigning the variable the value of outerHeight at the time it's not visible? I think you need to re-evaluate outerHeight after the toggle transition. Replace
alert(wrapNavHeight);
with
alert($("#wrapNav").outerHeight(true));
see if that's any better?
Nobody else actually explained why this happens. Here's why:
It depends on:
which object is used as "this" for invocation of the function containing all the code above
which object is used as "this" for invocation of the function defined starting on line 4 of your code
In JavaScript, "global" references actually apply to the current this object (or to the "true" global object (window in web browsers) if not within a function)
Thus, if the this objects for the 2 functions I pointed out above are different, then you'll get the situation you observed.
In the browser, the default this object is usually window, but this can be changed when the function is run, such as by passing a different parameter to apply or by calling the function as a method.
Been a year since I used jQuery seriously, but if I recall right, jQuery event handlers usually rebind this to something useful related to the event (using apply-- you can do this too).
So, assuming the outer function's this hasn't been bound to anything special other than window, simply replace wrapNavHeight with window.wrapNavHeight in the inner function to achieve your desired effect.
(In practice I wouldn't actually do this, though, as a matter of style. Just declare wrapNavHeight as a var within the outer function instead, and then you'll get lexical scoping.)
Try searching.
jQuery global variable best practice & options?