Basic Javascript Global Scope Issue - javascript

I'm having issues with scope in Javascript. Take a look at this code, for example:
$(function() {
var items = "GLOBAL";
$('.add').click(function() {
$.post("main/get", { 'get' : 'all' },
function(data){
items = String(data.result);
items = items.split(' *** ');
alert(items);
}, "json");
alert(items);
return false;
});
$(".add").autocomplete({
source: items
});
});
I'm trying to get autocomplete working, and it almost is. The only problem is that I can't seem to change items outside of the inner-most function. The first alert gives me what I'm looking for, but the second just gives me "GLOBAL." The bottom autocomplete part has to be able to access it.
Any help is appreciated!
Thanks!

It is not just a scope issue. Since your request is very likely to happen asynchronously (unless configured otherwise) it won't work that way anyway. You have to initialize the autocomplete in the callback function which gets called once your AJAX request is complete:
$(function() {
$('.add').click(function() {
$.post("main/get", { 'get' : 'all' },
function(data){
var items = String(data.result);
items = items.split(' *** ');
$(".add").autocomplete({
source: items
});
}, "json");
});
});

Related

Can't get value of <select> element in jQuery

I want to create a web app like google sheets in jQuery, but when I try to get a <select> value with $(select).val() I get `null.
I tried to put the code into $(button).click() and then it worked so I think the problem is that Javascript is executed before HTML, but I am not sure.
$(function() {
$.post("getTablesName.php", function(data) {
$("#tables_SCT").html(data);
});
var name = $("#tables_SCT").val();
$.get("getTable.php", { name: name }, function(data) {
$("#columns").html(data);
});
)};
I just want to get the $("#tables_SCT").val().
You will need to put the var name = $("#tables_SCT").val(); line inside the first AJAX callback. As it stands your code is trying to read content which doesn't yet exist in the DOM as the request which fills the option elements is asynchronous.
You will also need to make the second AJAX call from this point too, for the same reason. Try this:
$(function() {
$.post("getTablesName.php", function(data) {
$("#tables_SCT").html(data);
var name = $("#tables_SCT").val();
$.get("getTable.php", { name: name }, function(data) {
$("#columns").html(data);
});
});
)};

Select from json on click

I'm trying to select a row from a json array using jquery. This is what i have:
$(document).ready(function() {
$.getJSON( "js/collectie.json", function(data) {
jsoncollectie = data;
})
$( "#collectie li" ).click(function(){
var thumb_id = $(this).data("id");
for(var i = 0; i < jsoncollectie.stoelen.length; i++){
if(jsoncollectie.stoelen[i].ref == thumb_id){
$("#detailimage").attr('src', jsoncollectie.stoelen[i].image);
$("#detailimage").attr('title', jsoncollectie.stoelen[i].title);
$("#title").html('<h4> '+jsoncollectie.stoelen[i].naam+' </h4>');
$("#secondaryimage").attr('src', jsoncollectie.stoelen[i].secondaryimage);
$("#secondaryimage").attr('title', jsoncollectie.stoelen[i].secondarytitle);
$("#description").html('<p> '+jsoncollectie.stoelen[i].description+' </p>');
}
}
});
});
Now when i click on a list item (#collectie li) the console outputs "ReferenceError: jsoncollectie is not defined". I don't know why it's doing that and i'm pretty sure it worked two weeks ago. Don't know much about javascript/jquery yet, but i'm slowly learning.
$(document).ready(function()
{
// Provide access to data outside of the getJSON call
var m_oJsonCollectie = null;
// Get the data
$.getJSON( "js/collectie.json", function(data)
{
// Set the data
m_oJsonCollectie = data;
// Apply the click handler
$( "#collectie li" ).click(function()
{
var thumb_id = $(this).data("id");
for(var i = 0; i < m_oJsonCollectie.stoelen.length; i += 1)
{
if(m_oJsonCollectie.stoelen[i].ref == thumb_id)
{
$("#detailimage") .attr('src', m_oJsonCollectie.stoelen[i].image);
$("#detailimage") .attr('title', m_oJsonCollectie.stoelen[i].title);
$("#title") .html('<h4> '+ m_oJsonCollectie.stoelen[i].naam+' </h4>');
$("#secondaryimage").attr('src', m_oJsonCollectie.stoelen[i].secondaryimage);
$("#secondaryimage").attr('title', m_oJsonCollectie.stoelen[i].secondarytitle);
$("#description") .html('<p> '+ m_oJsonCollectie.stoelen[i].description+' </p>');
}
}
});
});
});
JS have block level scope, so you wont get the values outside of the function unless you provide access to them or they are declared in global scope (which is considered bad practice).
This pattern should help you keep your data accessible, and only applies the click handler if the getJSON call is successful.
Check that your getJSON request is being received and returned by using deferred methods
// Syntax that will shed light to your issue :
$.getJSON
(
"js/collectie.json",
function (oJSON) { /*success*/ }
)
.done(function() { /* succeeded */ })
.fail(function() { /* failed */ })
.always(function() { /* ended */ });
I came to this conclusion due to comments and the fact that a variable only declared in the success handler for getJSON was undefined. Since the JSON containing variable was undefined, the success handler must never have been called. Chances are that the path to the JSON you are trying to get is incorrect.
Documentation for the methods to accomplish :
getJSON
done
fail
always
UPDATE
Knowing that the response is 304, and the results are undefined are the important details here. This issue has been addressed by jQuery already here
This is actually correct, given the ifModified header has not been set to false.
To fix this issue, use ajaxSetup() to modify the header.
NOTE : the use of this method is not recommended by jQuery, but in this case it works.
// place this is document ready handler before making any calls.
$.ajaxSetup({ ifModified : false });

How to wait for a function to complete before executing another function

I'm using selecter jquery. I initialize it by typing the code
$("select").selecter();
I need to make sure that the formstone selecter jquery library has completed before i start appending elements. So what i did is to is use the $.when function
initialize: function(){
$.when($("select").selecter()).then(this.initOptions());
},
initOptions: function(){
this.$el.find('.selecter').addClass('something');
}
But this does not work. How can i wait while formstone selecter is doing its thing before i execute another function?
Thanks,
UPDATE
Here's the update of what i did but it does not work.
initialize: function(){
$("select").selecter({callback: this.initOptions });
},
initOptions: function(){
this.$el.find('.selecter').addClass('something');
}
There is a callback option.
The function passed as a callback will receive the newly selected value as the first parameter
Should be $("select").selecter(callback: function() { alert('callback fired.') });
or as shown
$("select").selecter({
callback: selectCallback
});
function selectCallback(value, index) {
alert("VALUE: " + value + ", INDEX: " + index);
}
The problem which I think regarding the callback edited code is that this can refer to anything. Try the following code
var selectorObj = {
initialize: function(){
$("select").selecter({callback: selectorObj.initOptions });
},
initOptions: function(){
this.$el.find('.selecter').addClass('something');
}
};
Created a working fiddler for you http://jsfiddle.net/6Bj6j/
The css is out of shape. Just select what is poping up when you click on the dropdown. You will get an alert which is written in the callback.
The problem with the provided snippet is the scope of the callback:
var selectorObj = {
initialize: function(){
$("select").selecter({ callback: selectorObj.initOptions });
},
initOptions: function(){
// 'this' refers to the "$('.selecter')" jQuery element
this.addClass('something');
}
};
However if you just need to add a class to the rendered element, you should use the 'customClass' option:
$("select").selecter({
customClass: "something"
});
If you need to do more, you can always access the Selecter element directly:
var $selecter = $("select").selecter().next(".selecter");
$selecter.addClass("something").find(".selecter-selected").trigger("click");
Sidenote: I'm the main developer of Formstone. If you have any suggestions for new features or better implementation, just open a new issue on GitHub.

Show loading animation when collapsible block is opening Jquery Mobile

I have a problem trying to show loading icon when collapsible block is opening. I have a collapsible block with a listview inside which is populated dynamically via ajax/php. They list might have up to 500 elements, so I would like to show loading animation while it is loading.
I have tried
$('div.century').live('expand', function(){
var idval = $(this).attr('id');
console.log('expanded'+idval);
$.mobile.showPageLoadingMsg ();
$.get("helpers/getByCentury.php", { id: idval},
function(data){
$("#"+idval+" ul.ulist").html(data);
$("#"+idval+" ul.ulist").listview('refresh');
});
$.mobile.hidePageLoadingMsg ();
});
I have also tried
$('div.century').live('expand', function(){
var idval = $(this).attr('id');
console.log('expanded'+idval);
$.mobile.pageLoading();
$.get("helpers/getByCentury.php", { id: idval},
function(data){
$("#"+idval+" ul.ulist").html(data);
$("#"+idval+" ul.ulist").listview('refresh');
});
$.mobile.pageLoading(true);
});
without any luck.
Can anyone tell me how to fix this?
Thanks in advance.
You want to call $.mobile.hidePageLoadingMsg() in the callback function for your ajax call:
$('div.century').live('expand', function(){
var idval = $(this).attr('id');
console.log('expanded'+idval);
$.mobile.showPageLoadingMsg ();
$.get("helpers/getByCentury.php", { id: idval},
function(data){
$("#"+idval+" ul.ulist").html(data);
$("#"+idval+" ul.ulist").listview('refresh');
$.mobile.hidePageLoadingMsg ();//NOTICE: this has been moved inside the callback function for your $.get() call
});
});
Also a couple pointers.
You are using the $("#"+idval+" ul.ulist") selector twice in a row, you can make that more efficient by chaining function calls together like so:
$("#"+idval+" ul.ulist").html(data).listview('refresh');
If other people view your webpage in a browser that does not have the console.log function they will get an error and your JS will stop running, it is normally a good idea to put calls to the console.log function inside a conditional that checks for the existance of that function:
if (typeof(console.log) == 'function') {
console.log('expanded'+idval);
}

Why does my array appear empty when calling a function after the array is built in JS

// variables to be used throughout
var videos = new Array();
// similar artist/bands
function similarTo(who) {
$.getJSON('http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&artist='+who+'&limit=20&api_key=b25b959554ed76058ac220b7b2e0a026&format=json&callback=?', function(data) {
$.each(data , function(i,similars) {
$.each(similars.artist, function(c, artist) {
$.getJSON('http://gdata.youtube.com/feeds/api/videos?q='+artist.name+'&orderby=relevance&start-index=1&max-results=1&v=2&alt=json-in-script&callback=?', function(data) {
$.each(data.feed.entry, function(i,video) {
videos.push({
id: video.id.$t.split(":")[3],
title: video.title.$t
});
});
});
});
initPlaylist();
});
});
}
// start the playlist
function initPlaylist() {
$('#ytplayerid').load('includes/ytplayer.php?track=' + videos[currenttrack].id);
$('#player span').html(videos[currenttrack].title);
}
When my code reaches the initPlaylist() function the videos array appears to be empty, I have a feeling its actually being fired before the $.getJSON() call... is this possible? If I add a console.log(videos) after each push() the array is actually being built.
$.each(similars.artist, function(c, artist) {
// doing ajax stuff here
$.getJSON('url', function(data) {
// this will get called later
$.each(data.feed.entry, function(i,video) {
videos.push({
id: video.id.$t.split(":")[3],
title: video.title.$t
});
});
});
});
// trying to manipulate ajax data now :(
initPlaylist();
Your videos is empty because your trying to manipulate it before it's ready.
What you want to do is use jQuery 1.5+ deferred objects
var ajaxs = $.map(similars.artist, function(artist, c) {
return $.getJSON('url', function(data) {
$.each(data.feed.entry, function(i,video) {
videos.push({
id: video.id.$t.split(":")[3],
title: video.title.$t
});
});
});
});
// when all the ajaxs finish then call init play list
$.when.apply($, ajaxs).then(initPlaylist);
Move initPlaylist to a point where videos exists:
function similarTo(who) {
$.getJSON('http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&artist='+who+'&limit=20&api_key=b25b959554ed76058ac220b7b2e0a026&format=json&callback=?', function(data) {
$.each(data , function(i,similars) {
$.each(similars.artist, function(c, artist) {
$.getJSON('http://gdata.youtube.com/feeds/api/videos?q='+artist.name+'&orderby=relevance&start-index=1&max-results=1&v=2&alt=json-in-script&callback=?', function(data) {
var videoes = []; //create array
$.each(data.feed.entry, function(i,video) {
videos.push({
id: video.id.$t.split(":")[3],
title: video.title.$t
});
});
initPlaylist();
//videos exists, but i think you might need to pass it as a parameter
});
});
});
});
}
Although, knowing what is in initPlaylist(); might help. And it might solve what appears to be a scope problem in your code.
ALSO: Ajax is asynchronous, there forit might not finish by the time the code gets to initPlaylist();, so you need some type of callback to call initPlaylist(); when all the ajax calls are done.

Categories

Resources