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.
Related
I have a problem understanding Javascript's engine technique called hoisting.
I've tried to create a var in the top of my file to quickly access and edit, but i want to use a variable that has yet to be declared.
First attempt:
var easy_to_edit_value = "some text " + a_yet_to_be_defined_var;
//imagine loads of code so its hard to find the correct funtion to edit the log
function my_function (a_yet_to_be_defined_var){
console.log(easy_to_edit_value);
}
my_function("more text");
This generates an error at line 1 since a_yet_to_be_defined_var is not defined.
After looking at this post: post-by-apsillers i tried again but this time declaring the var without value (so its known but undefined untill declared somewhere futheron)
var a_yet_to_be_defined_var; // now its known so this error is gone
var easy_to_edit_value = "some text " + a_yet_to_be_defined_var;
function my_function (a_yet_to_be_defined_var){
console.log(easy_to_edit_value);
}
my_function("more text");
//still undefined
//new attempt with a fresh var being set in the function before being called
var new_var;
var easy_to_edit_value = "some text " + new_var;
function my_function2 (a_yet_to_be_defined_var2){
new_var = a_yet_to_be_defined_var2;
console.log(easy_to_edit_value);
}
my_function2("more text");
//still undefined
But this outputs: some text undefined where i was expecting the some text more text since i filled the var before asking for it.
Please note these functions aren't ran using my_function("something") but are triggered by this: client.on('message', my_function);, i've seen arrow function solutions for related questions but i'm not sure how i would get that to work here.
Is it possible to make this work?
Instead of defining a value called easy_to_edit_value, change it to a function called easy_to_call_function that will return "some text " concatenated with the current value of new_var.
Once a (var or let) variable is assigned, it must be reassigned or reevaluated each time.
let new_var;
const easy_to_call_function = () => "some text " + new_var;
function my_function2(a_yet_to_be_defined_var2) {
new_var = a_yet_to_be_defined_var2;
console.log(easy_to_call_function()); // Call the function
}
my_function2("more text");
You need to re-declare the variable inside of the function:
var new_var;
var easy_to_edit_value = "some text " + new_var;
function my_function2(a_yet_to_be_defined_var2) {
new_var = a_yet_to_be_defined_var2;
easy_to_edit_value = "some text " + new_var;
console.log(easy_to_edit_value);
}
my_function2("more text");
If one variable is changed, other variable will not update according to the new value. That kind of programming requires a really high level compiler, which not many people are willing to make. The programmer must update variables on their own.
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
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.
I've the following script:
gapi.analytics.ready(function() {
viewSelector.on('viewChange', function update (data) {
var title = document.getElementById('view-name');
title.innerHTML = data.property.name + ' (' + data.view.name + ')';
activeUsers.set(data).execute();
renderWeekOverWeekChart(data.ids);
renderTopBrowsersChart(data.ids);
renderTopCountriesChart(data.ids);
setTimeout(function() {
var list = document.getElementsByTagName("tr")[0];
list.getElementsByTagName("th")[0].innerHTML = "Pagina's";
list.getElementsByTagName("th")[1].innerHTML = "Paginaweergaven";
}, 500);
});
});
And within the following code I would like to re-run the update(); function.
function datumwissel( datumbtn ) {
if ( datumbtn.className == 'maand' ) {
datumbtn.className = 'jaar';
dimensions1 = 'ga:month,ga:nthMonth';
start1 = moment(now).date(1).month(0).format('YYYY-MM-DD');
end1 = moment(now).format('YYYY-MM-DD');
start2 = moment(now).subtract(1, 'year').date(1).month(0).format('YYYY-MM-DD');
end2 = moment(now).date(1).month(0).subtract(1, 'day').format('YYYY-MM-DD');
format1 = 'M';
format2 = 'MMM';
update();
}
else {
datumbtn.className = 'maand';
dimensions1 = 'ga:date,ga:nthWeek';
start1 = moment(now).subtract(2, 'day').date(1).format('YYYY-MM-DD');
end1 = moment(now).format('YYYY-MM-DD');
start2 = moment(now).subtract(2, 'day').date(1).subtract(1, 'month').format('YYYY-MM-DD');
end2 = moment(now).subtract(2, 'day').date(1).subtract(1, 'day').format('YYYY-MM-DD');
format1 = 'YYYYMMDD';
format2 = 'Do';
update();
}
}
But somehow this doesn't work. I also tried in the above script:
window.update = function (data) {}. But that also doesn't work.
How can I call the update(); function that is situated inside the gapi.analytics.ready(function() {} ?
Important is that I cannot make it globally as it has to be situated inside the gapi.analytics.ready().
It's really a simple matter of moving the function declaration
function update (data) {
// same as existing code
}
gapi.analytics.ready(function() {
viewSelector.on('viewChange', update );
});
And passing in data needed when you call it in your other function
function datumwissel( datumbtn ) {
if ( datumbtn.className == 'maand' ) {
..........
update(datumbtn);
}.......
Important is that I cannot make it globally as it has to be situated inside the gapi.analytics.ready()
That's not actually true - you can have it global and there at the same time. Whether you want to, is a different manner, as that would pollute the global namespace and so on. However, here is how that can be achieved:
First, extract the update function outside of the ready handler like so
function update (data) {
var title = document.getElementById('view-name');
title.innerHTML = data.property.name + ' (' + data.view.name + ')';
activeUsers.set(data).execute();
renderWeekOverWeekChart(data.ids);
renderTopBrowsersChart(data.ids);
renderTopCountriesChart(data.ids);
setTimeout(function() {
var list = document.getElementsByTagName("tr")[0];
list.getElementsByTagName("th")[0].innerHTML = "Pagina's";
list.getElementsByTagName("th")[1].innerHTML = "Paginaweergaven";
}, 500);
}
This will create a new function with the name update which accepts one parameter called data. Thanks to hoisting it would not matter if it's before or after anywhere you want to use it, as it would be effectively "pulled" to the top.
Next, you can just use the function inside the ready handler like so:
gapi.analytics.ready(function() {
viewSelector.on('viewChange', update);
});
Since .on(events, handler) accepts a function as the second parameter, you can just provide a function reference there. It doesn't matter that your function is technically declared elsewhere, as it is still going to be called with the same arguments. Similarly, if you replace update with alert you will be giving the reference to window.alert so you will get an alert with data.
With that, you can just call the same function in your other piece of code.
That is true for any place that uses callbacks, including setTimeout - you can just give a function reference and it's going to be called. Internally, those kinds of functions almost always do something like callback() or callback(someData), occasionally callback.call(/* parameters */) where callback is the passed in argument. Whether you define that argument as you are calling the function, e.g., selector.on("click", function() {/* code */}) or separately, e.g.,
function clickHandler() { /* code */ }
selector.on("click", clickHandler)
matters little.
With that said, whether you want the function global is a different matter. Unless both pieces of code are in the same place, a global function may be the easiest way. You could, also, namespace anything your app uses, which would partially avoid the global pollution. Not completely, but sometimes you just need to have things living under window if you have multiple files, in which case, you can define your own little corner there to play with: window.myApp = window.myApp || {} would create a new object that can serve as namespace and so you will be able to do things like myApp.update = function(data) { /* code */ } and thus share that code.
If your two pieces of code are indeed in one file, then you merely need to create the function outside both using var update = function(data) { /* code */ } then hand it to each in the exact same way, since update is still going to be a function reference, however, if assigned to a variable, it won't be added to the global namespace (nor would the declaration be hoisted).
I'm needing some scope help. Below is my code (simplified). Within a function within the success event of an ajax call within buildDropdownOptions... (mouthful).
I'm attempting to set something in a settings object that's at the same level as buildDropdownOptions, yet I'm unable to access it (maybe because of the AJAX call?).
Does anyone know how to get this thing to work?
var settings = {},
buildDropdownOptions = function () {
var success = function (clinics, settings) {
var dropdownOptions = [];
$.each(clinics, function (i, clinic) {
dropdownOptions.push('<option value="' + clinic.ClinicId + '">' + clinic.Name + '</option>');
});
settings.dropdown.options = dropdownOptions;
};
$.ajax({
'url': settings.dropdown.source,
'success': function (clinics, settings) {
success(clinics);
}
});
};
By including settings as a function parameter, you are hiding the higher-level settings variable.
In fact, in this case you are hiding it with undefined.
Remove the settings mentioned in the argument list and it should work fine.
It seems that you cannot access settings.dropdown property. Try to change the first line of your code to:
var settings = {dropdown: {}}: