JavaScript variable empty without previous alert messge - javascript

I'm using the following function to create a list of links from a JSON-File and put them into a span element (jquery has been initialized)
$(document).ready(function() {
function createjson(category) {
var content = "";
i = 0;
$.getJSON(category + ".txt", function(data){
while(data[i] != null) {
content = content + '<li class="liste"><a target="_blank" class="liste" href="' + data[i].url + '">' + data[i].name + '</a></li>';
i++;
}
});
document.getElementById("list_" + category).innerHTML = content;
}
createjson("cat1");
});
However, if I use it like this, the span element remains empty. It is only when I replace
document.getElementById("list_" + category).innerHTML = content;
with
alert("test");
document.getElementById("list_" + category).innerHTML = content;
that I get the desired output. How can I get it without printing out an alert message beforehand?

That is becuase .getJSON() is an asynchronous method. Your code will try to update the DOM with content, before the server had time to respond to the request. Therefor no content is inserted.
With the alert, you stop the execution long enough for the server to respond, therefor it works when you add the alert.
The correct way to do it would be to move your content update into the success-callback instead, which is always run after the server has responded:
$.getJSON(category + ".txt", function(data){
while(data[i] != null) {
content = content + '<li class="liste"><a target="_blank" class="liste" href="' + data[i].url + '">' + data[i].name + '</a></li>';
i++;
}
$("#list_" + category).html(content);
});
I replaced your document.getElementById("list_" + category).innerHTML = content; with the somewhat shorter jQuery equivalent $("#list_" + category).html(content); in the above example as well.

It's not the alert which is fixing this, the alert is basically blocking the UI for long enough for the AJAX request to complete.
$.getJSON is an asyncronous method. This means that the result of the function call is not always available immediately, you'll need to move your code which sets the list element HTML inside the $.getJSON callback:
$.getJSON(category + ".txt", function (data) {
while(data[i] != null) { ... }
document.getElementById("list_" + category).innerHTML = content;
});

Put the html append in callback, also reset the i when call back starts.
$(document).ready(function() {
function createjson(category) {
$.getJSON(category + ".txt", function(data)
{
var content = "";
for(i=0; i<data.length; i++)
{
content = content + '<li class="liste"><a target="_blank" class="liste" href="' + data[i].url + '">' + data[i].name + '</a></li>';
}
document.getElementById("list_" + category).innerHTML = content;
});
}
createjson("cat1");
});

Use this !== to compare with not equal to NULL
while(data[i] !== null) {
content = content + '<li class="liste"><a target="_blank" class="liste" href="' + data[i].url + '">' + data[i].name + '</a></li>';
i++;
}

Related

create and append multiple elements with data from JSON

Let data be a JSON,
what I am trying here is appending multiple elements according to the data from JSON to the div ,
<div class="studentTab" ></div>. How can I achieve this ?
(nb: please consider that we have 2 Languages and 2 Specializations)
for (i = 0; i < 10; i++) {
var html = '<div class="dtls">' +
'' +
'<h4 class="verified">' + data.student[0].firstName + '</h4 >' +
'<ul>' +
'<li>' + data.student[0].location + '</li>' +
'<li>' + data.student[0].mark + ' /hour</li>' +
'<li>' + data.student[0].year+ ' accademic year</li>' +
if (data.student[0].Languages != null)
{
var lang = string.Join(",", data.student[0].Languages.Select(it => it.Name));
<li>lang </li>
}
if (data.student[0].Specializations.length > 1)
{
var rcount = data.student[0][index].Specializations.length - 1;
<li>data.student[0].Specializations[0].Name + rcount more</li>
}
else
{
<li>data.student[0].Specializations[0].Name</li>
}
'</ul>' +
'</div>'
$('#studentTab').append(html);
};
You may use map() for solving such problem. Please make check:
If your 'data' is underfined or not;
Better save your data.student[0] in some variable;
You never know how many objects inside of data you have. Probably some times is underfined due to network errors or more or less objects due to some back-end updates

How to return a variable from a callback after building variable with .getjson and .each

The Problem
I'm trying to figure out how to return HTML that I've built from a JSON file with jQuery.
I seem to have gotten returnLocations() to wait until getLocations() is finished so that the variable locationsBody is finalized with information gathered from my .each loop. The trouble (I think) is my not being able to return that variable to output it to my HTML page.
The Question
How can I return the variable locationsBody?
Note
(there may be errors in the below code as I trimmed it down as best I could but I think it should illustrate the problem with or without them)
The jQuery
the global variables
var locationsFull = 'un d fined';
var locationsOpener = '' +
'<div class="locations-header">places youve been</div>' +
'<div class="locations-container">' +
'<div class="locations-nav left">left</div>' +
'<div class="locations-nav right">right</div>'
;
var locationsBody = '<div class="locations-inner">'; // opening of container
var locationsCloser = '</div>'; // closing of container
the function
function locationsFunction() {
function getLocations() {
var wait = $.Deferred();
var area = 'Area1';
var counter = 1;
$.getJSON("locations.json", function(data) {
$(data.places).each(function() {
var location = this.location;
var image = this.image;
if (this.area === 'Area1') {
if (counter == 2) {
locationsBody = locationsBody +
'<div class="locations-places">' +
'<img src="images/places/' + image + '">' +
'<div class="locations-places-image">' + location + '</div>' +
'</div></div>'
;
counter = 0; // added closing of container, reset to 0
} else {
locationsBody = locationsBody +
'<div class="locations-places">' +
'<img src="images/places/' + image + '">' +
'<div class="locations-places-image">' + location + '</div>' +
'</div>'
;
counter = counter + 1;
}
}
})
wait.resolve();
})
return wait;
}
function returnLocations() {
locationsFull = locationsOpener + locationsBody + locationsCloser; // works, proven in alert and console.log
//alert(locationsFull); // works
console.log(locationsFull); // works
//return locationsFull; // doesnt work
//return 'anything'; // doesnt work
}
getLocations().then(returnLocations);
}
the call
$(function() {
$('.locations-body').html(locationsFunction());
})
The JSON File
{"places":[
{
"area": "Area1",
"location": "Downtown",
"image": "downtown.jpg"
},
{
"area": "Area1",
"location": "Uptown",
"image": "uptown.jpg"
}
]}
The HTML
<div class="locations-body"></div>
Further Note: Questions similar to this have been asked dozens of times on stackoverflow alone and those questions and answers have hundreds of thousands of reads. I have read through all of the top ones and more over the last 2 days. My problem is my inability to thoroughly understand the answers and apply them to my exact situation as seems to be the problem of the dozens (hundreds/thousands?) of people asking these questions and the hundreds of thousands (millions?) of people that have been searching for solutions to asynchronous problems.
You could just call .html() inside the returnLocations() function if that's viable.
the function
function returnLocations() {
locationsFull = locationsOpener + locationsBody + locationsCloser;
$('.locations-body').html(locationsFull);
}
the call
$(function() {
locationsFunction();
}
Otherwise you'll need to look into callbacks, read this, if you need to do it this way I can update my answer with an example later on.
Have you tried
return wait.promise();
instead of returning the Deferred?
Then calling like this:
var deferredChain = $.Deferred();
deferredChain.then(getLocations).then(returnLocations);
deferredChain.resolve();
I discovered today that simply putting a .done at the end of $.getJSON seems to work just the same and is much easier than using $.Deferred and the associated lines of code to make it work.
function locationsFunction() {
var area = 'Area1';
var counter = 1;
$.getJSON("locations.json", function(data) {
$(data.places).each(function() {
var location = this.location;
var image = this.image;
if (this.area === 'Area1') {
if (counter == 2) {
locationsBody = locationsBody +
'<div class="locations-places">' +
'<img src="images/places/' + image + '">' +
'<div class="locations-places-image">' + location + '</div>' +
'</div></div>'
;
counter = 0; // added closing of container, reset to 0
} else {
locationsBody = locationsBody +
'<div class="locations-places">' +
'<img src="images/places/' + image + '">' +
'<div class="locations-places-image">' + location + '</div>' +
'</div>'
;
counter = counter + 1;
}
}
})
}).done(function() {
locationsFull = locationsOpener + locationsBody + locationsCloser;
$('.locations-body').html(locationsFull);
});
}

why my for loop is infinite here

below is the js code for wikipedia search project. I am getting infinite for loop even though it had condition to stop repeating the loop. I am stuck in this problem.
$(document).ready(function() {
$('.enter').click(function() {
var srcv = $('#search').val(); //variable get the input value
//statement to check empty input
if (srcv == "") {
alert("enter something to search");
}
else {
$.getJSON('https://en.wikipedia.org/w/api.php?action=opensearch&search=' + srcv + '&format=json&limit=20&callback=?', function(json) {
$('.content').html("<p> <a href ='" + json[3][0] + "'target='_blank'>" + json[1][0] + "</a><br>" + json[2][0] + "</p>");
/*for loop to display the content of the json object*/
for (i = 1; i < 20; i++) {
$('p').append("<p><a href ='" + json[3][i] + "'target='_blank'>" + json[1][i] + "</a>" + json[2][i] + "</p>");
}
});
}
});
});
You are appending to each and every one of <p> in page.
Since your for loop appends even more <p> (and you possibly have a high number of <p> elements in your page beforehand) you overflow your call stack.
You probably wanted to append to a specific <p>. Try giving an id to your selector.
from what i can see in the url you need to do the following:
loop over the terms found and select the link based on the index of the element, chose a single element .contentto append the data not a set of elements p, this will increase the number of duplicated results
$.getJSON('https://en.wikipedia.org/w/api.php?action=opensearch&search='+srcv+'&format=json&limit=20&callback=?', function(json){
$.each(json[1],function(i,v){
$('.content').append("<p><a href ='"+json[2][i]+"'target='_blank'>"+json[0]+"</a>"+v+"</p>");
});
});
see demo: https://jsfiddle.net/x79zzp5a/
Try this
$(document).ready(function() {
$('.enter').click(function() {
var srcv = $('#search').val(); //variable get the input value
//statement to check empty input
if (srcv == "") {
alert("enter something to search");
}
else {
$.getJSON('https://en.wikipedia.org/w/api.php?action=opensearch&search=' + srcv + '&format=json&limit=20&callback=?', function(json) {
$('.content').html("<p> <a href ='" + json[3][0] + "'target='_blank'>" + json[1][0] + "</a><br>" + json[2][0] + "</p>");
/*for loop to display the content of the json object*/
var i = 1;
for (i; i < 20; i++) {
$('p').append("<p><a href ='" + json[3][i] + "'target='_blank'>" + json[1][i] + "</a>" + json[2][i] + "</p>");
}
});
}
});
});

ajax callback to insert image if exists

I've been following this answer in an attempt to write good asynchronous js - but I can't quite figure out what's going wrong for me.
I'm using autocomplete to request a specific set of files from the server, but for some datasets these files may not exist (i.e, there may be one, or up to four files). If they do exist, I want to append .html to include them.
The images don't load unless I add
async: false
to the .ajax call, which makes the callback redundant.
for (var i = 1; i < 5; i++) {
(function (counter) {
// define function with the callback argument
function ajaxTest(callback) {
$.ajax({
type: "GET",
url: counter + "_1.jpg",
success: function (result) {
callback(counter);
}
});
}
// call the function
ajaxTest(function (num) {
var image_temp = '<div class="thumbnail"> <img class="img-thumbnail" src="'+ num + '_1.jpg" /> Image' + num + '</div>';
console.log(image_temp); //check readout
image_html += image_temp;
});
})(i);
}
Then image_html contains html for only those images that exist.
$('#outputcontent').html(outer_html + image_html + outer_html_end);
Can anyone explain to my misunderstanding here please? Why isn't image_html being populated?
EDIT: 'full' code below. I use jquery autocomplete to pull from the stellar_comp array, then for those with properties that exist (some are blank), html is generated. Within that is the ajax call.
$(function () {
var stellar_comp = [
{
value:'X0005-28',
data: {
set0: {
aperture:'X2105-28',
secat:'X025-18',
set:'1',
run:'r06',
continuumFilter:'J',
narrowBandFilter:'638/28',
numberOfSources:'1',
priorityCode:'1'
etc... etc ...
},
},
];
$('#autocomplete').autocomplete({
lookup: stellar_comp,
onSelect: function (suggestion) {
var path_Value = 'data/' + suggestion.value + '/';
var outer_html = '<h1>' + suggestion.value + ' </h1> <div class="container">';
var outer_html_end = '</div>';
for (var category in suggestion.data) {
if (suggestion.data.hasOwnProperty(category)) {
if (suggestion.data[category].aperture) {
summary_table_contents = '<tr> <td>' + suggestion.data[category].set + '</td> <td>' + suggestion.data[category].run + '</td> <td>' + suggestion.data[category].continuumFilter + '</td> <td>' + suggestion.data[category].narrowBandFilter + '</td> <td>' + suggestion.data[category].numberOfSources + '</td> <td>' + suggestion.data[category].priorityCode + '</td></tr> ';
summary_table += summary_table_contents;
var aperturePlot = suggestion.data[category].aperture + '_' + suggestion.data[category].run;
var seCATPlot = suggestion.data[category].secat + '_Rsub_ss_' + suggestion.data[category].run;
var aperture_match = suggestion.data[category].aperture_match;
cog = path_Plots + aperturePlot + '_cog';
sbprof = path_Plots + aperturePlot + '_sbprof';
thumb_cog = '';
thumb_cog_temp = '';
thumb_sb = '';
temp='';
for (var i = 1; i < 5; i++) {
(function (counter) {
function some_function(callback) {
$.ajax({
type: "GET",
url: cog + counter + "_1.jpg",
async: false,
success: function (result) {
callback(counter);
}
});
}
some_function(function (num) {
var thumb_cog_temp = '<div class="col-lg-3 col-sm-4 col-xs-6"> <a class="thumbnail" target="_blank" href="' + cog + num + '_1.jpg"> <img class="img-thumbnail" src="' + cog + num + '_4.jpg" /></a> <div class="caption"><h5>' + suggestion.value + ':S' + num + '</h5></div></div>';
var thumb_sb_temp = '<div class="col-lg-3 col-sm-4 col-xs-6"> <a class="thumbnail" target="_blank" href="' + sbprof + num + '_1.jpg"><img class="img-thumbnail" src="' + sbprof + num + '_4.jpg" /></a><div class="caption"><h5>' + suggestion.value + ':S' + num + ' </h5></div></div>';
console.log(num, counter);
thumb_cog += thumb_cog_temp;
thumb_sb += thumb_sb_temp;
});
})(i);
}
cog_sbprofile_row='<div class="row"><h3>C o G</h3> ' + thumb_cog + '</div><div class="row"><h3>Profiles</h3> ' + thumb_sb + '</div>';
console.log(cog_sbprofile_row);
body_html += aperture_row;
body_html += seCAT_row;
body_html += aperture_match_row;
body_html += pixel_map_row;
body_html += skyprofile_row;
body_html += cog_sbprofile_row;
body_html += '<hr>';
};
};
};
top_html += summary_table + '</tbody> </table> </div></div> <hr>';
$('#outputcontent').html(outer_html + hipass_container + top_html + body_html + outer_html_end);
}
});
});
The current problem is due to using the resulting HTML string, before it has been created via multiple asynchronous calls. You need to either defer that operation until all loads are completed, or change the situation so it is not dependent on final completion (progressive loading looks busier anyway).
Personally I would simply insert placeholders for the images as you loop and insert each as they load. This way the order is retained. You can even do effects (fade etc) on each as they load:
var wrapper = "";
for (var i = 1; i < 5; i++) {
// Make a placeholder element for each image we expect
wrapper += '<div class="thumbnail" id="thumb_' + i + '">'
(function (num) {
// call the function
$.ajax({
type: "GET",
url: num + "_1.jpg",
success: function (result) {
var image_temp = '<img class="img-thumbnail" src="'+ num + '_1.jpg" /> Image' + num;
// Insert new image into unique element and fade it in
$('#thumb_'+num).append(image_temp).fadeIn();
}
});
})(i);
}
// Append all placeholders immediately - these will be appended to as each image is loaded
$('#outputcontent').html(outer_html + wrappers + outer_html_end);
Update:
As Alnitak points out, the whole exercise is a little pointless as you are ignoring the result returned from the Ajax call anyway, so you might as well drop the Ajax call (which adds no value) and simply build the image elements on the fly server-side :)
You have to do the appending after all ajax requests finish their jobs. First define this:
var after = function(reqs) {
var d = $.Deferred(),
cnt = reqs.length;
if (!cnt) {
d.resolve();
}
var limit = cnt; // separate var just in case reqs are synchronous
// so it won't mess up the loop
for (var i = 0; i < limit; i++) {
reqs[i].always(function() {
cnt--;
if (!cnt) {
d.resolve();
}
});
}
return d.promise();
};
You can use $.when instead of this but if one of the requests results in 404 it won't wait for others. Now:
var image_html = "",
requests = [];
for (var i = 1; i < 5; i++) {
(function (counter) {
// define function with the callback argument
function ajaxTest(callback) {
return $.ajax({
type: "GET",
url: counter + "_1.jpg",
success: function (result) {
callback(counter);
}
});
}
// call the function
var req = ajaxTest(function (num) {
var image_temp = '<div class="thumbnail"> <img class="img-thumbnail" src="'+ num + '_1.jpg" /> Image' + num + '</div>';
console.log(image_temp); //check readout
image_html += image_temp;
});
requests.push(req); // push the request to the array
})(i);
}
after(requests).done(function() {
$('#outputcontent').html(outer_html + image_html + outer_html_end);
});
Note that I've added return $.ajax(...).
BTW: what's the point of defining ajaxTest function? Just put everything in an anonymous callback.
Read more about deferred objects and promises here:
http://api.jquery.com/category/deferred-object/
http://joseoncode.com/2011/09/26/a-walkthrough-jquery-deferred-and-promise/
or just google it.
EDIT: if the order is important then you just have to tweak the callback function a bit:
var image_html = []; // array instead of string
// some code...
var req = ajaxTest(function (num) {
// ...
image_html[counter] = image_temp;
});
// some code...
after(requests).done(function() {
image_html = image_html.join("");
$('#outputcontent').html(outer_html + image_html + outer_html_end);
});

EDITED How to get the json value for localStorage in a loop

after Oyeme's advise, i have changed my code, but still cannot work. it can alert "testing" but cannot alert DBuser_name .
By the following coding, i can create a content like the image below. My problem is that, if the user click one of the people in the list, i would like to extract the student.data.user_name into localStorage. however, in my coding below, seems that i did wrong?
function getStudentList() {
$.getJSON('http://mydomain.com/getStudents.php?jsoncallback=?', function(data) {
$('#studentList li').remove();
$('#load').hide();
//students = data.user_name;
$.each(data, function(index, student) {
//$('#studentList').append('<li><a href="tutor_student_detail.html?user_name=' + student.data.user_name + '">' +
$('#studentList').append('<li><a href="tutor_student_detail.html">' +
'<h4>' + student.data.user_name + '</h4>' +
'<p>' + student.data.role + '</p>' +
'</a></li>');
$("li a").click(function() {
window.localStorage["view"] = $(this).data('user_name');
});
});
$('#studentList').listview('refresh');
});
}
Below are the coding of tutor_student_detail.html (js part)
function start(){
var localUsername=window.localStorage.getItem("view");
$.getJSON('http://mydomain.com/getStudent.php?user_name='+localUsername+'&jsoncallback=?', displayStudent);
alert("testing");
}
function displayStudent(data) {
var DBuser_name=data[0].data.user_name;
alert(DBuser_name);
var employee = data.item;
$('#username').text(student.data.user_name);
$('#pw').text(student.data.password);
$('#id').text(student.data.id);
$('#actionList').listview('refresh');
}
the output of json string is something like
?([{"data":{"id":"4","user_name":"studentB","book":"4567","role":"Student"}}]);
$('#studentList').append('<li><a href="tutor_student_detail.html"
data-name="'+student.data.user_name+'">' +
'<h4>' + student.data.user_name + '</h4>' +
'<p>' + student.data.role + '</p>' +
'</a></li>');
$("li a").click(function() {
window.localStorage["view"] = $(this).data('name');
});

Categories