It looks like my for loop is looping the last object parsed into all fields.
http://codepen.io/anon/pen/EKxNaN
This is the actual code I am using, I built something kind of similar for codepen since I cannot request JSON from the actual source on codepen.
var championMasteryPHP = "https://api.myjson.com/bins/xked";
$.getJSON(championMasteryPHP, function (json) {
for (var i = 0; i < json.length; i++) {
var champID = json[i].championId;
var champLevel = json[i].championLevel;
var pointstonextlevel = json[i].championPointsUntilNextLevel;
var championInfo = "http://example.com/champInfo.php?champid=" + champID;
$.getJSON(championInfo, function (json2) {
var champName = json2.name;
var champTitle = json2.title;
$('#champ').append("<li>ID: " + champID + " | Name: " + champName + " | Level: " + champLevel + " | Points to Next Level: " + pointstonextlevel + "</li>");
});
};
});
Long story short, what I am trying to achieve would look like this.
But for some reason, this is what I get instead.
The right names, but the other variables are the very last variable in the listing.
Is there a better way to do this? Am I doing something terribly wrong?
Thanks.
You are using Asynchronous Process inside a javascript for loop more info here
You can modify to use self executing function, so it will work
var championMastery = "https://api.myjson.com/bins/xked";
$.getJSON(championMastery, function (json) {
for (var i = 0; i < json.length; i++) {
(function(obj){
var champID = obj.championId;
var champLevel = obj.championLevel;
var pointstonextlevel = obj.championPointsUntilNextLevel;
var championInfo = "http://example.com/champInfo.php?champid=" + champID;
$.getJSON(championInfo, function (json2) {
var champName = json2.name;
var champTitle = json2.title;
$('#champ').append("<li>ID: "+champID+" | Name: " +champName+ " | Level: " +champLevel+ " | Points to Next Level: " +pointstonextlevel+ "</li>");
});
})(json[i])
};
});
A very good article which I came across recently. Detailing about the Javascript Scope ans Closures . This is a must know things for coding in javascript, Jquery. And the part where there is an explanation to this above problem is look for the topic under The Infamous Loop Problem
Now coming to the problem. Your code should actually look like
$.getJSON(championInfo, function (json2) {
(function(){
var champName = json2.name;
var champTitle = json2.title;
$('#champ').append("<li>ID: "+champID+" | Name: " +champName+ " | Level: " +champLevel+ " | Points to Next Level: " +pointstonextlevel+ "</li>");
})()
});
Note the change where I have added the self executing function. The problem you got the weird output with the same outputs for all the function is because.
Your function Inside the $.getJSON call back is just a function definition and the function is not executed at that moment. By the time your first getJSON hits its call back your for loop is complete, And the final value in the variables are of the last loop and hence when your first getJSONhits its callback it displays the values of the last loop.
To make it more simple. Write this in your console window.
function alert1(){ alert("I am one!!");}
And now call / execute the function by doing
alert1();
You will get a alert saying "I am one!!"
Now add this to the console
function alert1(){ alert("I am one, But overritten!!");}
And then try out calling it. The out put would be "I am one, But overritten!!", So the function actually gets overwritten and that's what is happening in the for loop. The last function is the one which is available for all the callbacks.
Related
I have a Firebase database set up like this:
>>XXX
>>>>dislike: 0
>>>>like: 1
In my web application, I can retrieve their value into console by:
var fb = new Firebase('https://xxx.firebaseio.com');
fb.child('like').once('value',function(snapshot){
console.log(snapshot.val());
});
fb.child('dislike').once('value',function(snapshot){
console.log(snapshot.val());
});
Now if I want to retrieve these values into the global scope, it will return undefined when I do this:
var fb = new Firebase('https://xxx.firebaseio.com');
var like = fb.child('like').once('value',function(snapshot){
return snapshot.val();
});
var dislike = fb.child('dislike').once('value',function(snapshot){
return snapshot.val();
});
Of course I have a silly solution to this problem, by putting entire script inside these two scopes - but it would be a disasters if I have hundreds of scopes to work with, and if I like to dynamically turn them on and off. Here is my solution:
var likeRef = new Firebase('https://xxx.firebaseio.com/like');
var dislikeRef = new Firebase('https://xxx.firebaseio.com/dislike');
likeRef.once('value',function(likeObj){
dislikeRef.once('value',function(dislikeObj){
var like = likeObj.val();
var dislike = dislikeObj.val();
});
});
Here is another answer suggested by Frank van Puffelen from the source <Passing variable in parent scope to callback function>, and it didn't quite work because seem to only work for script that is adding a new object in an array. Here is my attempt:
var like = 0;
var dislike = 0;
var val = 0;
var fb = new Firebase('https://xxx.firebaseio.com/');
function fb_like() {
fb.child('like').on('value', read_val);
return val;
}
function fb_dislike() {
fb.child('dislike').on('value', read_val);
return val;
}
function read_val(snapshot) {
var val = snapshot.val();
}
fb_like();
fb_dislike();
console.log(like);
console.log(dislike);
As you might expected, the console logs 0 and 0, instead of the values in like and dislike in firabase xxx database.
In fact, I took a step further and use array instead of integer value, and it still won't work:
var like = [0];
var dislike = [0];
var val = [0];
var fb = new Firebase('https://xxx.firebaseio.com/');
function fb_like() {
fb.child('like').on('value', read_val);
console.log('fb_like: ' + val[0]);
return val;
}
function fb_dislike() {
fb.child('dislike').on('value', read_val);
console.log('fb_dislike: ' + val[0]);
return val;
}
function read_val(snapshot) {
val[0].value = snapshot.val();
}
fb_like();
fb_dislike();
console.log('Like: ' + like[0]);
console.log('Dislike: ' + dislike[0]);
The console will logs:
fb_like: 0
fb_dislike: 0
Like: 0
Dislike: 0
This means probably means only adding (pushing) new objects into an array will work on a global scope, changing the value of an object will only effect the local scope.
Then, I realized even adding (pushing) new objects into an array cannot effect the global scope. Here is my attempt:
var like = 0;
var likeObj = [];
var fb = new Firebase('https://xxx.firebaseio.com/');
function fb_like() {
fb.child('like').on('value', read_like);
console.log('fb_like: ' + likeObj[0]);
return likeObj;
}
function read_like(snapshot) {
likeObj.push(snapshot.val());
console.log('likeObj: ' + likeObj[0]);
}
fb_like();
like = likeObj[0];
console.log('Like: ' + like);
As a result, the console logs:
fb_like: undefined
Like: undefined
likeObj: 1
This probably means the read_like() isn't effecting scopes larger than itself, event with array.push command.
I got this piece of code below which is not DRY. What i want to do is to cut it,so everything below var = text would be used only once not twice.
My concept is,to close these two functions in bigger function (e.g. guess()) and keep trimmed correctGuess() and incorrectGuess() within it.
Now here's the question,how can I call such nested function as describe above from outside scope. I was thinking about smth like: guess().correctGuess() which is obviously wrong but I wanted to share a concept.
Additionally, when e.g. correctGuess() would be called, is rest of the commands within our main guess() function would be executed?
function correctGuess(i) {
totalScore++;
questionNumber++;
var text = "Correct!";
var updatePage = ['<div id="answerDiv">' +
'<h1>' + text + '<h1>' +
'<h2>Total Score: ' + totalScore + '</h2></div>'
];
mainContent[html](updatePage);
$('#answerDiv')[fadeIn]("slow");
$('#answerDiv').append('<button id="nextButton">Next Question</button>');
$('#nextButton').on('click', function() {
if (questionNumber == allQuestions.length && totalScore <= 4) {
results()
} else {
question(questionNumber)
}
})
};
var incorrectGuess = function(i) {
totalScore--;
questionNumber++;
var text = "Wrong!";
var updatePage = ['<div id="answerDiv">' +
'<h1>' + text + '<h1>' +
'<h2>Total Score: ' + totalScore + '</h2></div>'
];
mainContent[html](updatePage);
$('#answerDiv')[fadeIn]("slow");
$('#answerDiv').append('<button id="nextButton">Next Question</button>');
$('#nextButton').on('click', function() {
if (questionNumber == allQuestions.length && totalScore <= 4) {
results();
} else {
question(questionNumber);
}
});
};
http://www.w3schools.com/js/js_objects.asp
From your question it seems like you aren't very familiar with object notation. Read up on the above link and then try to create a js "guess" object with 2 member functions. Correct and Incorrect guess.
You need to use the this keyword.
function guess(){
/* do stuff here for guess() */
this.correct = function(){
/* Do stuff for correct */
}
this.wrong = function(){
/* Do stuff for wrong */
}
return this;
}
Because you returned this you can now access the correct() and wrong() functions using:
guess().correct();
// AND
guess().wrong();
Note that whatever code you write inside guess() and outside the two nested functions will also be called every time you call guess().correct() or guess().wrong()
If you do not want any particular code to execute every time they "guess" regardless of right or wrong then I would suggest just storing the correct() and wrong() functions in an object literal.
var guess = {
correct: function(){
// Code for "correct" here
},
wrong: function(){
// Code for "wrong" here
}
}
And then you can access them using
guess.correct();
// AND
guess.wrong();
I'm very new to javascript, and can't figure this seemingly simple issue. I have an array of elements (say usernames) that i iterate over. i want to map these usernames to click event methods (i know this isn't the perfect way, but it's just something that seems to work):
this.events = this.events || {};
var self = this;
for (var i=0; i<arr.length; i++) {
console.log(data.user[i]); // < --- this username is correct
var username = data.user[i];
$(this.el).append("<td><button class='btn btn-primary save" + i + " btn-sm'>Save</button></td>");
var eventKeySave = 'click ' + '.save' + i;
this.events[eventKeySave] = function(){
(function (username) {
console.log("inside method save " + username); // <--- this username is wrong
self.onSave(username, 'something')
})(username);
};
}
this.delegateEvents();
now, in my onSave method, it just merely prints the usernames:
onSave: function(name, extras) {
console.log("you clicked save on " + name + " , " + type); // < -- wrong name
}
But why do i ALWAYS get only the LAST username of the array in my onSave function?
for example, if my array looks like [ 'david', 'emily', 'stephanie', 'michelle'], only michelle gets passed in to the onSave function, despite the fact that each button's click event should have been set with the respective name in the array.
i even tried to pass the username by value but it still doesn't work. what is going on in Javascript that i'm not seeing?
You need a closure:
this.events[eventKeySave] = (function (name) {
return function(){
console.log("inside method save " + name); // <--- this username is now ok
self.onSave(name, 'something');
};
})(username);
It happens because the function is called after that the for loop ended, so the value of "username" is the last value of the array in all cases.
Add a closure save the actual value of 'username' for that returned function
This is a three part question.
In the code shown below, whenever I execute the functions (walk, run, crawl) I am observing that it is displaying the output for the method distance_travelled in a cumulative manner:
Trey says thank you
Trey walked a distance of 3
Trey ran a distance of 13
Trey crawled a distance of 16
Trey ran a distance of 26
I would like to ensure that each function calculates the distance by considering the method distance_travelled to be initialized to 0.
My second question is related to the callback function.
I am trying to create another property/method called doSomething() and have this method return a random function back (walk, run, crawl).
For example if I execute the following code:
var returned_function = person.doSomething();
returned_function();
It should execute one of the three methods. I have managed to execute the method run(). However, when I run the code in my browser, the alert pop up message displays undefined. Also, I encounter the same issue as in my first question. It calculates distance_travelled in a cumulative manner. How can I solve this?
My third question. I am trying to add a new method called 'fly' to the person object. The 'fly' method takes two functions as arguments.
I have to give a 30% chance for the person to fly. The function fly method should execute if the person is successfully able to fly (30% chance that this can happen). The second function should execute if the person is NOT able to fly (70% chance this would happen).
How can I implement this functionality into my code? Can someone suggest how to approach this problem?
<script type="text/javascript">
var person = new Object();
person.name = "Trey";
person.distance_travelled = 0;
person.say_name = alert(person.name);
person.say_something = function(xyz) {
document.write(person.name + " says " + xyz + '<br>');
}
person.say_something("thank you");
person.walk = alert(person.name + " is walking");
function walk(){
person.distance_travelled +=3;
document.write(person.name + " walked a distance of " + person.distance_travelled + '<br>');
}
walk();
person.run = alert(person.name + " is running");
function run(){
person.distance_travelled +=10;
document.write(person.name + " ran a distance of " + person.distance_travelled + '<br>');
}
run();
person.crawl = alert(person.name + " is crawling");
function crawl(){
person.distance_travelled +=3;
document.write(person.name + " crawled a distance of " + person.distance_travelled + '<br>');
}
crawl();
person.doSomething = function(abc){
alert(run());
}
var returned_function = person.doSomething();
returned_function();
</script>
First of all:
person.walk = alert(person.name + " is walking");
probably does nothing like what you think it would (because I can't ever think a line like that might make sense).
Your first question is trivial. If you want to output 3, just output 3, not distance_travelled.
Second question:
var activities = ['run', 'walk', 'crawl'];
person.doSomething = function() {
var randomActivity = activities[Math.floor(Math.random() * activities.length)];
return function() {
this[randomActivity]();
}
}
var personDoActivity = person.doSomething();
personDoActivity();
or
person.doSomething = function() {
var activities = [this.run, this.walk, this.crawl];
var randomActivity = activities[Math.floor(Math.random() * activities.length)];
return randomActivity;
}
var doActivity = person.doSomething();
doActivity.call(person);
Third question (and I'm changing it from object-oriented to procedural because you're not using OO correctly, anyway):
function maybeFly(fly, noFly) {
if (Math.random() < 0.3) {
return fly();
} else {
return noFly();
}
}
Because I still can't comment on answers -.-
#Amadan
the second version of doSomething can maybe be even nicer using .bind()
person.doSomething = function() {
var activities = [this.run, this.walk, this.crawl];
var randomIndex = Math.floor(Math.random() * activities.length);
return activities[randomIndex].bind(this);
}
var doActivity = person.doSomething();
doActivity();
Also worth noting that it is better to use, and more in the spirit of javascript to write
var person = {};
instead of
var person = new Object();
I declare a variable, outside of a function like so:
var vitalsValuesChecked = [];
then inside of a function I do:
vitalsValuesChecked.push('foobar');
In a later function I need to loop through the array for the items pushed, and constantly am not getting the result I expect. So inside that same function, I added console.log(vitalsValuesChecked); which returns [].
EDIT Code sample below;
EDIT 2 Fixed code below
var vitalsValuesChecked = [];
$(document).delegate("#hv-byresult-btn", "click", function () {
var vitalsTypeList = ['bp', 'ht', 'wt', 'pulse', 'resp', 'temp'];
vitalsValuesChecked = [];
for (var i = 0;i < vitalsTypeList.length;i++) {
if (document.getElementById(vitalsTypeList[i]).checked == true) {
vitalsValuesChecked.push(vitalsTypeList[i]);
console.log(vitalsTypeList[i] + " is checked. Adding to global array");
}
}
$('#vitals-measures-content').empty();
navigate("#vitals-measures");
for (var i = 0;i < vitalsValuesChecked.length;i++) {
console.log("vitalsValuesChecked at index " + i + " is " + vitalsValuesChecked[i]);
}
readRec('clinicalObservation', null, sortVitalsByResult);
});
function foobar() {
console.log(vitalsValuesChecked); //return []
for (var i=0;i < vitalsValuesChecked.length;i++) {
var valueSelected = vitalsValuesChecked[i];
console.log("Value of vitalsValuesChecked at index " + i + " is " + vitalsValuesChecked[i]);
}
}
You have defined vitalsValuesChecked twice which is a problem. One is global and one is local to the delegate() callback. The local definition overrides the global definition so when you thought you were setting values into the global variable, you were not - you were just changing the local variable which has a limited lifetime and thus your data was not available later in the global variable.
You should remove the
var vitalsValuesChecked = [];
inside the delegate handler so all modifications occur on the single global variable.
The var vitalsValuesChecked = []; inside the function will create a local variable. I don't think you want this if you're trying to push to the global variable.