Scope question, var undefined in nested function - javascript

I return a json map from a servlet using this get call. I create a var i = 0; to count the users returned in the data. I am easily able to use i as in jsondata[i] to access the users returned. But then below I try to use the same json and var inside the nested function of a click handler and it gives undefined. I looked here and it says:
Each function has its own scope, and any variable declared within that function is only accessible from that function and any nested functions. Because local scope in JavaScript is created by functions, it’s also called function scope. When we put a function inside another function, then we create nested scope.
so why is i undefined in the click handler ? If I change console.log(jsondata[i]); to console.log(jsondata[0]); I see the correct information rather than undefined.
$.get("/lod1/Scuttlebutt",{name:"onload",data: -1},function(jsondata) {
var numberofusers = jsondata.length;
var i = 0;
$(".mate").each(function(index) {
//show
$(this).css("display","block");
//construct new id
var mateid = 'mate'+jsondata[i][0].userid;
//create ids for this mate
$(this).attr('id',mateid+"MateContainer");
$(this).find(".mateavatar").attr("id",mateid+"avatar");
$(this).find("#matename").attr("id",mateid+"Name");
$(this).find("#matepoints").attr("id",mateid+"Points");
$(this).find("#matesensignia").attr("id",mateid+"Ensignia");
$(this).find("#matebio").attr("id",mateid+"Bio");
$(this).find("#matesets").attr("id",mateid+"Sets");
$(this).find("#matebadges").attr("id",mateid+"Badges");
//fill in correct info
$(this).find("#"+mateid+"Name").text(jsondata[i][0].userName);
$(this).find("#"+mateid+"Points").text(jsondata[i][0].points);
$(this).find("#"+mateid+"Ensignia").attr('src',calculateEnsignia(jsondata[i][0].rank));
//add onclick handler to username
$(this).find("#"+mateid+"Name").on('click',function(){
console.log(jsondata[i]);
var num_tabs = $("div#tabmenu ul li").length + 1;
$("div#tabmenu ul").append(
"<li><a href='#tab" + mateid + "'>" + jsondata[i][0].userName + "</a></li>"
);
$("div#tabmenu").append(
"<div id='tab" + mateid + "'> NEW TAB!</div>"
);
$("div#tabmenu").tabs("refresh");
});

This code will only work after the click event
console.log(jsondata[i])
What you can do , you can use any other ways to determine the index of your row then you can use it to get your data

Related

Cant access a global variable

I am getting an undefined when I try the post to twitter function. Should the quote_text variable be global and therefore accessible by the quoteTwitter function?
$(document).ready(function () {
loadJSON();
getQuote();
console.log(quote_text);
});
// Declare variables
var json_obj;
var num = 0;
var quote_text = "";
// Display a quote - this method is not perfect since the random number will repeat itself and it appears as if no new quote is delivered
function getQuote(callback) {
var html = "";
num = randNum();
quote_text = json_obj[num].quote;
html += "<strong> " + quote_text + " </strong>";
$("#quote").html(html);
$("#author").html(json_obj[num].author);
};
// Post the current quote on twitter
function quoteTwitter(quote_text){
var tweet = quote_text;
window.open('https://twitter.com/home?status=' +encodeURIComponent(tweet),"_blank");
}
Your function definition includes quote_text as a parameter, so inside the function it's trying to use that instead of the global variable with the same name. You're presumably not passing anything to the function when you call it, so it comes out as undefined.
You can fix this by changing this:
function quoteTwitter(quote_text){
to this:
function quoteTwitter(){
...but it'd probably be better in the long run to pass the correct value in as a parameter, if possible, instead of depending on global variables.

Trying a closure the wrong way?

I can't figure out what's wrong with the following code and why would it work when I do it using the second way. Can anyone please help me understand this?
I have this following Javascript code:
var clsFunc = function(prefix) {
var id = 0;
return function() {
id = id + 1;
console.log(prefix + id);
}
}
First way (did not work):
If I try to call this function like this nothing happens
clsFunc('div')
Second way (worked)
var getId = {'div': clsFunc('div')}
getId.div()
Result:
div1
undefined
getId.div()
Result:
div2
The clsFunc function creates a function and returns it. So just doing
clsFunc('div');
...is pointless, because it creates a function and then just throws it away, because you didn't store it anywhere.
The second way stores the function that was created in an object property. That function has a reference to the context that created it (the call to clsFunc), even though that call has returned, which contains the id variable. When you call the function (getId.div()), it adds 1 to id and then outputs the prefix ("div") followed by the new value if id (1, then 2, then, 3, etc.).
You don't need an object for your second way, you could just use a variable:
var clsFunc = function(prefix) {
var id = 0;
return function() {
id = id + 1;
console.log(prefix + id);
}
};
var f = clsFunc('div');
f(); // "div1"
f(); // "div2"
(The undefineds you're seeing are just because you're running this in a JavaScript console that shows you the result of calling the function; since the function doesn't return anything, the result of calling it is undefined.)

ajax request with user input trouble with variable definition

I've built a simple html input so that users can input a zip code and then I have a variable in javascript set to that input. I can console.log this to prove the variable is set and that it is a string. I then try to run an ajax call and sub in the zip but it doesn't work. I can console.log the variable at any stage and see the variable has been updated, but somehow it's a hoisting issue or something where the ajax call value 'userInputZip' always reads to what I initially set. The ajax call works when 'userInputZip' is initially set to a valid zipoAny help is appreciated.
$(document).ready(function(){
});//end of document.ready
var inputDate = '2015-12-04T20:00:00';
var inputZipCode = '60618';
var userInputZip;
function runAjax(){
console.log(userInputZip);
$.ajax(getJambaseData);
}
// var dataArr = [];
var getJambaseData = {
type: 'get',
url:
'http://api.jambase.com/events?zipCode='+userInputZip+'&api_key=[inserted my key here]',
// 'http://api.jambase.com/events?zipCode='+userInputZip+'&api_key=[inserted my key here]',
success: function (data){
for (i=0; i< 10; i++){
if(data.Events[i].Date == inputDate){
var shortDate = data.Events[i].Date.substring(0,10);
var shortTime = data.Events[i].Date.substring(11,19);
// dataArr.push(data.Events[i].Date, data.Events[i].Artists[0].Name);
$("#divID").append('</p>' + 'date::: '+ shortDate + ' time:::' + shortTime + ' show::: ' + data.Events[i].Artists[0].Name + ' time::: ' + data.Events[i].Date + ' address::: ' + data.Events[i].Venue.Address + ' city::: ' + data.Events[i].Venue.City + '</p>');
}
}
},
error: function(){
console.log('failed');
},
}
function findShows(){
var userZip = document.getElementById("userInput");
userInputZip = userZip.value;
document.getElementById("divID").innerHTML = userInputZip;
runAjax();
}
////////////////////
You've mentioned
but somehow it's a hoisting issue or something where the ajax call value 'userInputZip' always reads to what I initially set
You define getJambaseData as a variable when the script is initially executed. You set the url value to url:
'http://api.jambase.com/events?zipCode='+userInputZip+'&api_key=[inserted my key here]'. What else did you expect to happen?
That's like saying var x = 10; and expecting it to magically change when you call a function.
What you have to do is move the whole var getJambaseData = {...} initialization into runAjax function and it should fix it. Or you could skip the variable initialization part and just pass the part inside {...} (including the curly braces obviously) inside the $.ajax call instead of variable. If you look at jQuery docs you'll see that in most examples and it's the usual syntax.
Not related to your question, but here are some friendly words of advice:
Don't use variable before you define it (reading top to bottom), it will save you a lot of headaches.
Another recommendation is don't use so much global variables, you could get the userInputZip inside findShows function and pass it to runAjax as function argument. If you'll develop applications in a way where you rely on global state a lot, you'll have a bad time very soon.

passing parameters to a functionX from functionY running in a loop based on link clicked

function loadGroups() {
new Ajax.Request("https://www.xyz.com/groups.json", {
method: 'get',
onSuccess : function(transport) {
var response = JSON.parse(transport.responseText);
for (var i=0; i<response.length; i++) {
var hostname = response[i];
var tr = new Element("tr", { id: hostname });
var link = new Element("a");
link.setAttribute("href", id=hostname);
link.innerHTML = hostname;
# PROBLEM
# This onClick always sends response[-1] instead of response[i]
link.onclick = function() { loadHosts(hostname); return false; };
var td1 = new Element("td", { className: hostname });
link.update(hostname);
td1.appendChild(link);
tr.appendChild(td1);
table.appendChild(tr);
}
}
});
}
response = ['john', 'carl', 'jia', 'alex']
My goal is in this case 4 links should be displayed on the page and whenever a link is clicked the corresponding value should be passed to function - loadHosts. But the problem is always the last value gets sent as parameter to loadHosts function. The display of links is fine it is just the click on these links passing always the last element of array. I also tried loadHosts(link.href) , loadHosts(link.id)
Your problem is with the way closures work, you have a click handler inside a for loop that is using a variable defined inside the for loop. By the time the click handler runs, your variable has a different value than when you attached the handler because the loop has finished and it now has a different value.
Try something like this:
for (var i=0; i<response.length; i++) {
var hostname = response[i];
//...
link.onclick = (function(hn) {
return function() { loadHosts(hn); return false; };
})(hostname);
}
This uses a immediately invoked function expression (IIFE) to copy the value of hostname to the a new variable hn during the loop. Now when the handler is executed, it will be bound to the correct value.
Here's a blog post that explains the problem (and solution).
Also note that for loops don't define a variable scope. Variables defined in your for loop belong to the enclosing function scope. So hostname exists outside of your for loop which is why it holds the value of the last cycle through the loop in your click handler rather than the value when you attached the handler. It also explains why link.href doesn't work either. The variable link is attached to the enclosing function scope too rather than limited to the loop.
you should really go with the solution of #MattBurland but if you like, you could also tie the variable to the object itself.
link.hostname = response[i];
link.onclick = function() { loadHosts(this.hostname); return false; };
see simple example here: http://jsfiddle.net/xeyqs/

Global Variable Not Assigning between Functions

I am writing an extension for Google Chrome in HTML/Javascript. I am trying to use a global variable to pass information between two functions, however even if I assign my variable in one function it hasn't changed when I read it from the other function.
var type = 0; //define global variable
window.onload=function(){onCreated()}; //set onCreated function to run after loading HTML
function onCreated()
{
chrome.history.search({'text': ''},function(historyItems){gotHistory(historyItems)});//search for historyItems and then pass them to the gotHistory function
}
function gotHistory(historyItems)
{
var idcount=0;//used to increment the ids of each new element added
for(var count=0; count < historyItems.length; count++)//go through each history item
{
chrome.history.getVisits({'url':historyItems[count].url}, function(visitItems){gotVisits(visitItems)}); //search for visitItems for the url and pass the results to gotVisists function (atm all this function does is assign the global variable to =3)
var body = document.getElementById("outputid");//find the body of the HTML
var newt = document.createElement("p");//create a new element
newt.setAttribute("id","url"+idcount);//give it a unique id
newt.innerHTML = historyItems[count].title;//set the text to say the title of the url
if(type != 0)//if the other function was successful, type=3 and the text should be green
{
newt.style.color="green";
}
body.appendChild(newt);//add the new element to the body
idcount++;
}
}
function gotVisits(visitItems)
{
//assign the global variable to be 3
type = 3;
}
But, the elements are NEVER green. They should always be green. This means that in the function gotVisits, type is not being successfully assigned to 3.
Can anyone explain what is going on here?
Cheers,
Matt
p.s I realise the gotVisits function is useless here really, but I'm using it to demonstrate a point. In reality I will use it to pass back useful information to
Rather than:
var type = 0;
Try:
window.type = 0;
Optionally you can also use a closure such as:
(function() {
var type = 0;
var type = 0; //define global variable
window.onload=function(){onCreated()}; //set onCreated function to run after loading HTML
function onCreated()
{
chrome.history.search({'text': ''},function(historyItems){gotHistory(historyItems)});//search for historyItems and then pass them to the gotHistory function
}
function gotHistory(historyItems)
{
var idcount=0;//used to increment the ids of each new element added
for(var count=0; count < historyItems.length; count++)//go through each history item
{
chrome.history.getVisits({'url':historyItems[count].url}, function(visitItems){gotVisits(visitItems)}); //search for visitItems for the url and pass the results to gotVisists function (atm all this function does is assign the global variable to =3)
var body = document.getElementById("outputid");//find the body of the HTML
var newt = document.createElement("p");//create a new element
newt.setAttribute("id","url"+idcount);//give it a unique id
newt.innerHTML = historyItems[count].title;//set the text to say the title of the url
if(type != 0)//if the other function was successful, type=3 and the text should be green
{
newt.style.color="green";
}
body.appendChild(newt);//add the new element to the body
idcount++;
}
}
function gotVisits(visitItems)
{
//assign the global variable to be 3
type = 3;
}
})();
This saves you from polluting the window object, which you should avoid doing anyhow and allows the inner functions access to the type variable.
It should do what you want.
Also the outer function wrapper for your window.onload is redundant, just do:
window.onload = onCreated;
It looks like chrome.history.getVisits executes the callback asynchronously, so you first try to check that variable, and it gets updated later. You can verify this with a pair of console.log messages.
Move the rest of the code inside the callback, so it gets executed at the right time.

Categories