This is a scope issue, correct?
EDIT: http://jsfiddle.net/RPwLK/7/
On line 38 of the following function, where displayBossMessage() is called by itself, the variable "boss" seems to be out of scope. It should contain the name (string) of the current boss, and when the function is called, it should put it back in the "boss" argument.
But boss seems to always be undefined. I've tried creating a variable right before the jQuery listener is created to see if it would be within scope, but it seems as if it isn't.
Or maybe I'm just having a slow day. ;p
function displayBossMessage(boss,message,options,timer){
boss = bosses[boss];
//clear any possible lingering text/buttons
$(boss.messagebox).text('');
$(boss.optionsbox).text('');
displayMessage_CurrentBoss = boss;
//if no options provided, set a default "continue" button
if(options == ''){
options = {
'default' : {
'text' : 'Continue',
'func' : function(){}
}
}
}
$('#container div').hide();
$(boss.div).fadeIn(1500);
writeMessage(message,$(boss.messagebox),0);
setTimeout(function(){
$(boss.optionsbox).fadeIn(1000);
},3000);
//"listen" for a choice
var i = 0;
for(option in options){
$(boss.optionsbox).html($(boss.optionsbox).html() + '<button name="'+ i +'">'+ options[option].text +'</button> ');
$(document).on('click', (boss.div)+' button[name="'+i+'"]', function(){
options[option].func();
//close message screen or show defined response
if(typeof options[option].response != 'undefined'){
displayBossMessage(boss,options[option].response,'',true);
}else{
$(boss.div).hide();
$('#container div').fadeIn(1500);
}
});
}
if(timer){
//if they are afk/don't click after a minute, do it for them
setTimeout(function(){
$(boss.div+' button[name="0"]').click();
},60000);
}
}
Hope I'm not being completely oblivious and missing something so simple.
*Edit: Bosses variable (is global) *
(updated jsfiddle revision link to #11 which includes both solutions)
Looks like this could be a working fiddle: http://jsfiddle.net/RPwLK/11/
A minor problem: you have an extra ' on line 30 with the (second) alert call - the string literal was not closed correctly (or rather another was being opened). After that I was able to investigate and come up with the following conclusion (2 problems)...
The first problem was with the variable override here:
function displayBossMessage(boss,message,options,timer){
boss = bosses[boss]; // this line, boss was a string, now it will be an object
And the later usage in the same function here:
if(typeof options[option].response != 'undefined'){
displayBossMessage(boss,options[option].response,'',true); // first arg is now an object
The solution is to create a reference to the original boss when it was a string like:
function displayBossMessage(boss,message,options,timer){
var origBoss = boss; // let's remember what it was in its original string form
boss = bosses[boss];
And use it like so:
if(typeof options[option].response != 'undefined'){
displayBossMessage(origBoss,options[option].response,'',true); // now we're dealing with a string ref
The second problem is the reference to option within the for loop. It was always referencing the last value since the $(document).on('click'... is always delayed (asynchronous). There are a number of ways to solve this. I chose to use bind and pass in an argument with a reference to the value of option for each specific iteration.
Notice that in the original option is in the async function but not in a closure (for is not a closure):
for(option in options){
//...
$(document).on('click', (boss.div)+' button[name="'+i+'"]', function(){
options[option].func(); // here option is always the last item in options
So introduce an argument conveniently called option in the callback function for the click handler:
$(document).on('click', (boss.div)+' button[name="'+i+'"]', function(option){ // here
options[option].func(); // and now option is whatever the argument value is
And don't forget to pass it in the function declaration via bind:
$(document).on('click', (boss.div)+' button[name="'+i+'"]', function(option){
options[option].func();
// ...
}.bind(this,option)); // here we're passing whatever option is for the specific iteration as the first argument of the callback function
Note, this is just the scope, and each subsequent parameter for bind (see MDN) after the first corresponds to the arguments in the function definition.
Related
I don't know if you've ever felt like you've dived off the stupid tree and hit every branch on the way down, but JavaScript has that effect on me, an experienced PHP programmer but just doesn't get JS sometimes.
so I wrote this function using jQuery.extend({ .. }) as follows:
loadSection: function(obj){
if(typeof obj=='undefined')obj=event.target; //but this doesn't work
if(typeof obj=='string')obj=document.getElementById(obj);
var params=null;
var instr={};
var k=0;
while(true){
..etc..
I want to be able to call it two ways:
$.loadSection($('#employee-data-173'));
//or $loadSection('employee-data-173') for convenience
or:
$('#employee-data-173').loadSection();
Clearly (to you anyway!) I'm not grasping the concept of what's being passed in the second case. And that's not even getting into a case like this:
$('.someElement').click(loadSection);
You want to use the same function in three totally different usecases. In each case different arguments are automatically passed to the function. So first declare and prepare it to handle all possible types of arguments:
function loadSection(index, obj, args) {
var element, params = null;
if (obj && obj.nodeType) { // for usecase 2
element = obj; if (typeof args == 'object') params = args;
} else {
if (typeof obj == 'object') params = obj;
if (typeof index == 'string') { // usecase 1 with selector-string
element = document.getElementById(index);
else if (index.jquery) { // usecase 1 with jQuery object
if (index.length == 1) element = index[0]; // if only one element inside jQuery
// if more than one element there call this function recursively on each
else index.each(loadSection);
}
else if (index.target) element = index.target; // for usecase 3
}
/* your stuff */
}
In your usecase 1 you have to add the function to the global jQuery object and call it by $.loadSection(argument), where argument may be a id-selector-string or an jQuery-object $("selector"). There are two ways with identic result:
$.loadSection = loadSection;
$.extend({loadSection: loadSection});
In usecase 2 you want to call the function as a method of a jQuery object. Therefore you have to add it to the jQuery.prototype like so:
$.fn.loadSection = function( args ) { // $.fn is synonym for jQuery.prototype
return this.each(loadSection, args);
};
If you call now $("selector").loadSection() the function is executed once for each element matched by "selector". Arguments index and obj are automatically passed to loadSection.
In usecase 3 the function is used as callback-function for an event. Since its prepared for this case, the event object is automatically passed to it. So just do:
$('.someElement').click(loadSection);
You can use all cases mixed in the same piece of code.
EDIT "What shall/can the function return?"
It may return whatever you want to. Only the behaviour depends on usecase.
In usecase 1 you can do: var result = $.loadSection("selector") and result gets the returned value.
In usecase 2 there is an iteration over all elements in the jQuery object. The return value of loadSection is simply ignored except you explicitely return false. Then iteration stops. So if you do if (index == 2) return false; the function is executed maximum 3 times even when you have 7 elements inside jQuery object.
The function as a whole always returns the jQuery object, so you can do chaining:
$("selector").loadSection().css({color: 'blue'}).animate(...). ...
In usecase 3 the function is only executed but the return value gets never recognized anywhere, so you can't catch it.
EDIT 2 "How to pass additional params to the function?"
1) Now loadSection is prepared to take additional args (see above). 2) The setup of $.fn.loadSection is modified to take args (see above). 3) args must be an object or array eg {prop: 'color', id: 23} (otherwise it's ignored) but may be omitted.
In usecase 1 pass args as second argument
var result = $.loadSection("selector", args); // you find args inside in ""var params"
In usecase 2 args is the one and only possible argument. jQuery now makes an iteration inside an iteration: the function is called on each element once for each item in args!
The inner iteration is stoppable by return false, but the iteration over all elements no longer.
$("selector").loadSection({prop: 'color', id: 23}) // if $() contains 3 elems, function run six times
In usecase 3 its impossible to pass args since you only point to the function by its name.
Good day all.
I have this problem: I must provide a way to pass some optional arguments to a javascript function, this is pretty easy, ok. I decide to make all the arguments be an object, so anyone who need to call the function must provide the name of the arguments. But most of the time, the editors simply call the functions with the whole argument object, without omitting the empty arguments, because arguments are passed to the page via another way (let's say in the querystring, but it doesen't matter).
here it is how I have made the function:
function playAnimation(options) {
var timeOutAnimation = options.animationDuration || 15;
var backgroundColor = options.color ; // this argument will be always present, so no default.
//more code...
}
and this is the call (I use a php code for example purposes)
$(document).ready(function(){
var opt = {
animationDuration: '<?php echo $_GET['stopAnimationAfter'] ?>', //could be a number, or nothing.
color: 'black'
}
playAnimation(opt);
});
the problem is that the argument is passed and it seams that it never falls on the default value. I can't change the way the function is called (i.e. checking the avialability of the "get" parameter and build the function call accordingly, I must find a way to test the arguments inside my javascript.
is there a safe way to do so, like options.animationDuration === "undefined" (doens't work) or something?
SOLUTION: putting ' ' around each argument in the call, makes the argument be full or empty accordingly. and then i can test them.
You can use:
function playAnimation(options) {
options = options || {}
var timeOutAnimation = options.animationDuration || 15;
var color = options.color;
}
To set options to an empty object if it doesn't exist. You can also check for undefined like this if you want:
if(typeof options === "undefined") {
// options not passed
}
Here is a fiddle which demonstrates.
I'm building a phonegap project using jQuery mobile.
I have a javascript object that I'm iterating through.
Currently the problem is this:
Below is a method in my model object. It is self recursing, and once called, will recurse through itself to the next level every time a user clicks on a list item generated by the previous level of the object.
What I am battling with is passing the iterated segment, b, into the method itself as an object. For some reason this is returned as a string called [Object], and not the object itself.
This function does work as it's displaying the first level, but something about the "firstString" string I am creating for each child seems to be turning my object into a string named object. I have removed the quotes, placed the object in braces, to no avail.
Would anyone have any idea why this is happening, I'm obviously missing something important regarding passing objects into methods whose call is generated as a string...
My code is below, and line causing the issue is firstString+="model.recurseAppTree('"+b+"');";
recurseAppTree: function(AppTree)
{
$.each(AppTree, function(a,b)
{
var firstString='<li data-role="list-divider" role="heading" data-theme="b">'+b.DisplayValue+'</li>';
if(b.Children != null)
{
$.each(b.Children, function(c,d)
{
firstString+="<li data-theme='c'><a data-transition='slide' id='id-"+d.IdValue+"' href='javascript:void(0);'>"+d.DisplayValue+"</a></li>";
firstString+="<script>";
firstString+="$('#id-"+d.IdValue+"').click(function(){";
firstString+="model.recurseAppTree('"+b+"');";
firstString+="});";
firstString+="</script>";
});
}
$("#selectview").html(firstString);
$("#selectview").listview('refresh', true);
});
},
It's just normal.
You use an object in a string context by the concatenation with +. This tells JS to implicitely cast the object to a string.
b = {}
alert(typeof b) // object
alert(typeof (''+b)) // string
You should use event delegation for your gui
1- Add a (common) class to your '' tags, e.g. unrollLink :
var firstString='<li ...><a class="unrollLink" ...></a></li>"
2- Choose a node in your html, which is a parent of all your "tree" nodes, and will always be present in your html. Delegate the click handler to this node :
$('#selectview').on('click', '.unrollLink', function(){
//this === clicked link - write a function which returns the node you want based on the "id" you set
var myNode = getNode( this.id );
model.recurseAppTree( myNode );
});
3- change your function to produce the adequate html. You don't need to add code for the click events :
recurseAppTree: function(AppTree)
{
$.each(AppTree, function(a,b)
{
var firstString='<li data-role="list-divider" role="heading" data-theme="b">'+b.DisplayValue+'</li>';
if(b.Children != null)
{
$.each(b.Children, function(c,d)
{
// add the class you chose to the clickable items :
firstString+='<li data-theme="c"><a class="unrollLink" data-transition="slide" id="id-'+d.IdValue+'" href="javascript:void(0);">'+d.DisplayValue+'</a></li>';
});
}
$("#selectview").html(firstString);
$("#selectview").listview('refresh', true);
});
},
Is it possible to find the name of an anonymous function?
e.g. trying to find a way to alert either anonyFu or findMe in this code http://jsfiddle.net/L5F5N/1/
function namedFu(){
alert(arguments.callee);
alert(arguments.callee.name);
alert(arguments.callee.caller);
alert(arguments.caller);
alert(arguments.name);
}
var anonyFu = function() {
alert(arguments.callee);
alert(arguments.callee.name);
alert(arguments.callee.caller);
alert(arguments.caller);
alert(arguments.name);
}
var findMe= function(){
namedFu();
anonyFu();
}
findMe();
This is for some internal testing, so it doesn't need to be cross-browser. In fact, I'd be happy even if I had to install a plugin.
You can identify any property of a function from inside it, programmatically, even an unnamed anonymous function, by using arguments.callee. So you can identify the function with this simple trick:
Whenever you're making a function, assign it some property that you can use to identify it later.
For example, always make a property called id:
var fubar = function() {
this.id = "fubar";
//the stuff the function normally does, here
console.log(arguments.callee.id);
}
arguments.callee is the function, itself, so any property of that function can be accessed like id above, even one you assign yourself.
Callee is officially deprecated, but still works in almost all browsers, and there are certain circumstances in which there is still no substitute. You just can't use it in "strict mode".
You can alternatively, of course, name the anonymous function, like:
var fubar = function foobar() {
//the stuff the function normally does, here
console.log(arguments.callee.name);
}
But that's less elegant, obviously, since you can't (in this case) name it fubar in both spots; I had to make the actual name foobar.
If all of your functions have comments describing them, you can even grab that, like this:
var fubar = function() {
/*
fubar is effed up beyond all recognition
this returns some value or other that is described here
*/
//the stuff the function normally does, here
console.log(arguments.callee.toString().substr(0, 128);
}
Note that you can also use argument.callee.caller to access the function that called the current function. This lets you access the name (or properties, like id or the comment in the text) of the function from outside of it.
The reason you would do this is that you want to find out what called the function in question. This is a likely reason for you to be wanting to find this info programmatically, in the first place.
So if one of the fubar() examples above called this following function:
var kludge = function() {
console.log(arguments.callee.caller.id); // return "fubar" with the first version above
console.log(arguments.callee.caller.name); // return "foobar" in the second version above
console.log(arguments.callee.caller.toString().substr(0, 128);
/* that last one would return the first 128 characters in the third example,
which would happen to include the name in the comment.
Obviously, this is to be used only in a desperate case,
as it doesn't give you a concise value you can count on using)
*/
}
Doubt it's possible the way you've got it. For starters, if you added a line
var referenceFu = anonyFu;
which of those names would you expect to be able to log? They're both just references.
However – assuming you have the ability to change the code – this is valid javascript:
var anonyFu = function notActuallyAnonymous() {
console.log(arguments.callee.name);
}
which would log "notActuallyAnonymous". So you could just add names to all the anonymous functions you're interested in checking, without breaking your code.
Not sure that's helpful, but it's all I got.
I will add that if you know in which object that function is then you can add code - to that object or generally to objects prototype - that will get a key name basing on value.
Object.prototype.getKeyByValue = function( value ) {
for( var prop in this ) {
if( this.hasOwnProperty( prop ) ) {
if( this[ prop ] === value )
return prop;
}
}
}
And then you can use
THAT.getKeyByValue(arguments.callee.caller);
Used this approach once for debugging with performance testing involved in project where most of functions are in one object.
Didn't want to name all functions nor double names in code by any other mean, needed to calculate time of each function running - so did this plus pushing times on stack on function start and popping on end.
Why? To add very little code to each function and same for each of them to make measurements and calls list on console. It's temporary ofc.
THAT._TT = [];
THAT._TS = function () {
THAT._TT.push(performance.now());
}
THAT._TE = function () {
var tt = performance.now() - THAT._TT.pop();
var txt = THAT.getKeyByValue(arguments.callee.caller);
console.log('['+tt+'] -> '+txt);
};
THAT.some_function = function (x,y,z) {
THAT._TS();
// ... normal function job
THAT._TE();
}
THAT.some_other_function = function (a,b,c) {
THAT._TS();
// ... normal function job
THAT._TE();
}
Not very useful but maybe it will help someone with similar problem in similar circumstances.
arguments.callee it's deprecated, as MDN states:
You should avoid using arguments.callee() and just give every function
(expression) a name.
In other words:
[1,2,3].forEach(function foo() {
// you can call `foo` here for recursion
})
If what you want is to have a name for an anonymous function assigned to a variable, let's say you're debugging your code and you want to track the name of this function, then you can just name it twice, this is a common pattern:
var foo = function foo() { ... }
Except the evaling case specified in the MDN docs, I can't think of any other case where you'd want to use arguments.callee.
No. By definition, an anonymous function has no name. Yet, if you wanted to ask for function expressions: Yes, you can name them.
And no, it is not possible to get the name of a variable (which references the function) during runtime.
Below is my code fragment:
<div onclick = "myClick('value 1')">
button 1
</div>
<div onclick = "myClick('value 2')">
button 2
</div>
Basically when I for each click on a different div, a different value will be passed to the JavaScript function.
My Question is how can I keep track of the value passed in the previous click?
For example, I click "button 1", and "value 1" will be passed to the function. Later, I click on "button 2", I want to be able to know whether I have clicked "button 1" before and get "value 1".
Just add it to a variable in your script:
var lastClicked;
var myClick = function(value) {
lastClicked = value;
};
You can define somekind of variable, like var lastUsed;
add additional line to your function:
var lastUsed = null;
function myClick(value){
prevClicked = lastUsed; //get the last saved value
...
lastUsed = value; //update the saved value to the new value
...
}
And here you go
You need a variable. Variables are like little boxes in which you can store values. In this case, we can store the value that was last passed to the function myClick.
In Javascript, you can define a variable like this:
var lastClickedValue;
You can "put" a value into that variable. Let's say you want to put your name in there. You would do this:
lastClickedValue = 'sams5817';
Now here's the tricky bit. Variables have "scope". You might want to think about it as their "life-time". When a variable reaches the end of its scope, you cannot read or write to it anymore. It's as if it's never been. Functions define a scope. So any variable you define in a function will disappear at the end of the function. For example:
function myClick(value)
{
var lastClickedValue;
alert('lastClickedValue is = ' + value);
lastClickedValue = value;
}
That looks almost right, doesn't it? We declared a variable, display its last value, and update it with the new value.
However, since the lastClickedValue was declared in the function myClick, once we've reached the end of that function, it's gone. So the next time we call myClick, lastClickedValue will be create all over again. It will be empty. We call that an "uninitialized" variable.
So what's the problem? We're trying to remember a value even after the end of myClick. But we declared lastClickedValue inside myClick, so it stops existing at the end of myClick.
The solution is to make sure that lastClickedValue continues to exist after myClick is done.
So we must delcare lastClickedValue in a different scope. Luckily, there's a larger scope called the "global scope". It exists from the moment your page loads, and until the user moves on to another webpage. So let's do it this way:
var lastClickedValue;
function myClick(value)
{
alert('lastClickedValue is = ' + value);
lastClickedValue = value;
}
It's a very small difference. We moved the declaration of the variable lastClickedValue to be outside the function myClick. Since it's outside, it will keep existing after myClick is done. Which means that each time we call myClick, then lastClickedValue will still be there.
This will let you know what the last value passed to myClick was.
Finally, I'd like to advise you to look for some kind of Javascript tutorials. I wish I knew of some good ones to recommend, but I'm certain you can find a few on the Internet. If you try to write programs before understanding what you're doing, you'll find yourself producing work that is less than what you're capable of. Good luck!
I suppose you need something like this
var clickedButtons = [];
function myClick(value){
...
clickedButtons.push(value);
...
}
I am surprised that no one else mentioned this, but since functions are first class objects in JavaScript, you can also assign attributes and methods to functions. So in order to remember a value between function calls you can do something like I have with this function here:
function toggleHelpDialog() {
if (typeof toggleHelpDialog.status === 'undefined')
toggleHelpDialog.status = true;
else
toggleHelpDialog.status = !toggleHelpDialog.status;
var layer = this.getLayer();
if (toggleHelpDialog.status) layer.add(helpDialog);
else helpDialog.remove();
layer.draw();
}
Here I have added an attribute named 'status' to the toggleHelpDialog function. This value is associated with the function itself and has the same scope as the toggleHelpDialog function. Values stored in the status attribute will persist over multiple calls to the function. Careful though, as it can be accessed by other code and inadvertently changed.
we can leverage javascript static variables
One interesting aspect of the nature of functions as objects is that you can create static
variables. A static variable is a variable in a function‘s local scope whose value persists across
function invocations. Creating a static variable in JavaScript is achieved by adding an instance
property to the function in question. For example, consider the code here that defines a function
doSum that adds two numbers and keeps a running sum:
function doSum(x,y){
if (typeof doSum.static==='undefined'){
doSum.static = x+y;
}else{
doSum.static += x+y;
}
if (doSum.static >= 100){doSum.static = 0;doSum.static += x+y;}
return doSum.static;
}
alert(doSum(5,15))
alert(doSum(10,10))
alert(doSum(10,30))
alert(doSum(20,30))