OK, so I cannot seem to be able to change the global variable of systemPath after it goes through the ajax.It will work inside of ajax, but I need that updated variable outside of ajax. basically I'm trying to create an array of paths from xml and use them to locate other xml files that I can generate a table from.
Does anyone know what's going on here? Does ajax run before the variable is set and that is why I get an array length of 0 after the ajax?
var systemPath = new Array();
var techDigestArr = new Array();
var addToArray = function(thisarray, toPush){
thisarray.push(toPush);
}
$.ajax({
url: fullPath+"technical/systems/systems.xml",
dataType: ($.browser.msie) ? "text" : "xml",
success: function(data){
var xml;
if (typeof data == "string") {
xml = new ActiveXObject("Microsoft.XMLDOM");
xml.async = false;
xml.loadXML(data);
} else {
xml = data;
}
$(xml).find("system").each(function(){
var urlString = fullPath + "technical/system_" + $(this).attr("id") + "/" + $(this).attr("id") + "tech-digest.xml <br />";
//alert(urlString);
$("#td-articles").append(systemPath.length + urlString);
addToArray(systemPath,urlString);
//systemPath.push(urlString);
});
$("#msg-output").append("total - " +systemPath.length);//Returns 48
},//END SUCCSESS
error: function(){
alert("Sorry - ");
history.go(-1);
}
});//END AJAX CALL
$(document).ready(function(){
//$("#msg-output").append("total - " + systemPath.length); Returns 0
});
The ajax is ran asynchronously. Things execute in this order in your code.
stuff before $.ajax()
$.ajax() initiates an ajax call (while waiting for the response it continues to run the rest of the code)
stuff after $.ajax()
success callback
Note that depending on how fast the call is 3 and 4 might occur in reverse order (not the case here)
So when $(document).ready() is executed the ajax call might not have returned yet, so the code in the success callback didn't have a chance to execute. If you are lucky and have a fast connection than maybe the response will come before document ready, but it's unlikely.
Just so you can see that the global variable gets updated you can set a timeout:
$(document).ready(function(){
setTimeout(function(){
$("#msg-output").append("total - " + systemPath.length);
//if the delay set below is more than the time between the ajax request and the server response than this will print the correct value
},2000);
});
Related
Basically, I am trying to debug a code that was written long back by someone.
I have a jsp which is calling a javascript function on click of a button. In the javascript there is an ajax call happening to the Struts Action class. The Action class returns an Array List of objects to the jsp. My Array List has an inner list of objects too. Everything works fine when the list is small. But when the returned list is big, the ajax variable "data" returns as empty after a long time.
i think it is something to do with handling large data in the response. When i googled a few forums, i think the size of the response is exceeding a maximum limit and then returned as empty. When the response is small the variable "data" returns with a html reponse. But when it is huge, the variable has nothing.
Below is the ajax call. Since this is happening in production right now, any quick help is really appreciated. BTW, i am using Websphere 8.5.5.
function submitCriteria(submitSrc) {
var nameOfFormToPost = "criteriaForm";
var spanId = "resultDataSpan";
var formAsString = getFormAsString(nameOfFormToPost, submitSrc);
$.ajax({
type: "POST",
dataType: 'html',
url: "matchReconcileHome.do?execute=viewMatchResult",
data: formAsString,
success: function(data, textStatus) {
idx = data.indexOf("<title>XXXXXXX- XXXXX - Login</title>");
if (idx > 0 && idx < 500) {
window.location = "/pdrWeb/Login.jsp";
}
//Split the text response into Span elements
spanElements = splitTextIntoSpan(data, spanId);
//Use these span elements to update the page
replaceExistingWithNewHtml(spanElements);
$("#matchResult").treeTable();
setPageNavigation();
setResultControls();
//alert("Ajax called back Done" );
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert("Problem with server response:\n " + textStatus + ", Error: " + errorThrown);
}
});
return false;
}
Is it possible to get the modified timestamp of a file using just JavaScript?
I use a JSON file to fill a page by javascript and I would like to show the timestamp of that JSON file.
You can do it if you're retrieving the file through true ajax (that is, through XMLHttpRequest), provided you configure your server to send the Last-Modified header when sending the data.
The fundamental thing here is that when you use XMLHttpRequest, you can access the response headers. So if the server sends back Last-Modified, you can use it:
var xhr = $.ajax({
url: "data.json",
success: function(response) {
display("Data is " + response.data + ", last modified: " + xhr.getResponseHeader("Last-Modified"));
}
});
Just tried that on Chrome, Firefox, IE8, and IE11. Worked well (even when the data was coming from cache).
You've said below that you need to do this in a loop, but you keep seeing the last value of the variable. That tells me you've done something like this:
// **WRONG**
var list = /*...some list of URLs...*/;
var index;
for (index = 0; index < list.length; ++index) {
var xhr = $.ajax({
url: list[index],
success: function(response) {
display("Data is " + response.data + ", last modified: " + xhr.getResponseHeader("Last-Modified"));
}
});
}
The problem there is that all of the success callbacks have an enduring reference to the xhr variable, and there is only one of them. So all the callbacks see the last value assigned to xhr.
This is the classic closure problem. Here's one solution:
var list = /*...some list of URLs...*/;
list.forEach(function(url) {
var xhr = $.ajax({
url: url,
success: function(response) {
display("Data for " + url + " is " + response.data + ", last modified: " + xhr.getResponseHeader("Last-Modified"));
}
});
});
Since each iteration of the forEach callback gets its own xhr variable, there's no cross-talk. (You'll need to shim forEach on old browsers.)
You said below:
I already thought about a closure problem, thats why I used an array xhr[e] in my loop over e...
But your example doesent help...
and linked to this code in a gist:
//loop over e....
nodename=arr[e];
node_json=path_to_node_json+nodename;
html +='data</td>'
+'</tr>';
xhr[e] = $.ajax({
url: node_json,
success: function(response) {
$('#host_'+nodename).append("last modified: " + xhr[e].getResponseHeader("Last-Modified"));
}
});
That still has the classic error: Your success function closes over the variable e, not the value it had when the success function was created, and so by the time the success function runs, e has the last value assigned to it in the loop.
The forEach example I gave earlier fits this perfectly:
// (I assume `node_json`, `html`, and `path_to_node_json` are all declared
// here, outside the function.)
arr.forEach(function(nodename) {
var xhr; // <=== Local variable in this specific call to the iteration
// function, value isn't changed by subsequent iterations
node_json=path_to_node_json+nodename;
html +='data</td>'
+'</tr>';
xhr = $.ajax({
url: node_json,
success: function(response) {
// Note: You haven't used it here, but just to emphasize: If
// you used `node_json` here, it would have its value as of
// the *end* of the loop, because it's not local to this
// function. But `xhr` is local, and so it isn't changed on
// subsequent iterations.
$('#host_'+nodename).append("last modified: " + xhr.getResponseHeader("Last-Modified"));
}
});
});
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
AJAX- response data not saved to global scope?
I basically have a loop that contacts a script on my site using AJAX and then updates a string with the response from that script. Here's the code:
// Image code array
var result_url = 'http://localhost/view/';
for(i = 0; i < urls.length; i++) {
// Add URL to queue
$('#url_queue').append('<div class="uploadifyQueueItem"><div class="cancel"><img src="/assets/img/cancel.png" /></div><span class="fileName">' + image_name_from_url(urls[i]) + '</span><div class="uploadifyProgress"><div class="uploadifyProgressBar"></div></div></div>');
// Make a request to the upload script
$.post('/upload', { url: urls[i], username: username }, function(response) {
var response = jQuery.parseJSON(response);
if(response.error) {
alert(response.error);
return;
}
if(response.img_code) {
result_url += response.img_code + '&';
}
});
}
console.log(result_url);
The Firebug console just shows http://localhost/view/ when the string is logged. It's like the img_code response from my upload script isn't being appended to the string at all. I have tried logging the value of result_url within the $.post() method and that works fine, but the value is not being saved properly because it doesn't show later in my code. Is this a scope problem? Will I have to define result_url as a global variable?
Thanks for any help.
You are checking console.log(result_url); before the AJAX requests complete.
AJAX requests are (by default) run asynchronously. What that means is that your script continues to run while the request is still being made to the server.
Your callback function (provided to $.post as the 3rd parameter) is the one that get's executed after your AJAX request has completed.
Also note, that your AJAX request callback functions are called when the request is done. Your requests might not finish in the same order that they started. You could prevent all this by setting async:false, but that'll halt all of your javascript execution.
Another option would be to collect the jqXHR objects being returned by $.post, and then call $.when().done(), so that your console.log(result_url) happens only when all the AJAX requests are resolved:
// Image code array
var result_url = 'http://localhost/view/',
jqHXRs = [];
for(i = 0; i < urls.length; i++) {
// Add URL to queue
$('#url_queue').append('<div class="uploadifyQueueItem"><div class="cancel"><img src="/assets/img/cancel.png" /></div><span class="fileName">' + image_name_from_url(urls[i]) + '</span><div class="uploadifyProgress"><div class="uploadifyProgressBar"></div></div></div>');
// Make a request to the upload script
jqHXRs.push($.post('/upload', { url: urls[i], username: username }, function(response) {
var response = jQuery.parseJSON(response);
if(response.error) {
alert(response.error);
return;
}
if(response.img_code) {
result_url += response.img_code + '&';
}
}));
}
$.when.apply(this, jqHXRs).done(function(){
console.log(result_url);
});
This is because you're doing the console.log immediately after firing the Ajax. Since Ajax is asynchronous, the success function will not necessarily be called before the code which follows your ajax code.
jQuery's Ajax tools provide a way of calling ajax synchronously by including the async:false option. Try replacing your ajax call with:
$.ajax({
url:'/upload',
data:{ url: rls[i], username:username },
success:function(response) {
var response = jQuery.parseJSON(response);
if(response.error) {
alert(response.error);
return;
}
if(response.img_code) {
result_url += response.img_code + '&';
}
},
method:"post",
async:false
});
That way, code which follows you Ajax call would only be executed after the ajax completes.
Remember, though that this will lock up your page for the duration of the Ajax. Maybe it would be easier to just put the console.log(result_url); at the end of the success callback.
You're logging the result_url after the loop, but the $.post upload request may not have been complete yet. What I would recommend is to put the code that uses result_url inside a continuation call back and call it after you know that the last post request has completed.
e.g.
function continuation_code(result_url) {
// all your code that uses result_url goes here.
}
var result_url = 'http://localhost/view/';
var num_results_returned = 0;
for(i = 0; i < urls.length; i++) {
// Add URL to queue
$('#url_queue').append('<div class="uploadifyQueueItem"><div class="cancel"><img src="/assets/img/cancel.png" /></div><span class="fileName">' + image_name_from_url(urls[i]) + '</span><div class="uploadifyProgress"><div class="uploadifyProgressBar"></div></div></div>');
// Make a request to the upload script
$.post('/upload', { url: urls[i], username: username }, function(response) {
var response = jQuery.parseJSON(response);
if(response.error) {
alert(response.error);
return;
}
if(response.img_code) {
result_url += response.img_code + '&';
}
num_results_returned += 1;
if (num_results_returned == urls.length) {
continuation_code(result_url);
}
});
}
I want to change the XMLHttpRequest send function so that a function is called before the request is made and after the request is complete. Here is what I have so far:
var oldSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
//this never gets called
oldOnReady = this.onreadystatechange;
this.onreadystatechange = function() {
oldOnReady();
ajaxStopped();
}
ajaxStarted();
// according to http://www.w3.org/TR/XMLHttpRequest/
// there's only ever 0 or 1 parameters passed into this method
if(arguments && arguments.length > 0) {
oldSend(arguments[0]); //gets to here, calls this method, then nothing happens
} else {
oldSend();
}
}
function ajaxStarted() {
ajaxCount++;
document.getElementById("buttonClicky").innerHTML = "Count: " + ajaxCount;
}
function ajaxStopped() {
$("#isRunning")[0].innerHTML = "stopped";
ajaxCount--;
document.getElementById("buttonClicky").innerHTML = "Count: " + ajaxCount;
}
However, I'm doing something wrong here because once it hits the oldSend() call, it never returns or triggers the onreadystatechange event. So I must be doing somethingclickCount wrong here. Any ideas? I set a breakpoint and it gets hit just fine when I call this:
$.ajax({
type: "GET",
url: "file.txt",
success: function(result) {
//this never gets called
document.getElementById("myDiv").innerHTML = result;
}
});
So my new function is getting called. I guess just don't know how to call the old function. Any ideas on how to fix this code so that the Ajax Request is actually made and my new callback gets called?
Note: I'm aware of the JQuery events that essentially do this. But I'm doing this so I can get it to work with any Ajax call (Mootools, GWT, etc). I am just happening to test it with Jquery.
You need to call old functions in the context of this.
E.g.: oldSend.call(this)
Like others before me I'm struggling with scope in Javascript. (That and trying to read the darn stuff). I have checked some of the previous threads on this question but I cant seem to get them to apply correctly to my issuue.
In the example below, I want to manipulate the values in the tagsArr array, once the array has been fully populated. I declared the tagsArr variable outside the scope of the function in which it is populated in order to access it globally. But the variable doesn't seem to have the scope I expect - tagsArr.length is 0 at the point where I call output it to console on line 16.
$(function(){
var apiKey = [myapikey];
var tags = '';
var tagsArr = new Array();
$.getJSON('http://api.flickr.com/services/rest/?&method=flickr.people.getPublicPhotos&api_key=' + apiKey + '&user_id=46206266#N05&extras=date_taken,tags&format=json&jsoncallback=?', function(data){
$.each(data.photos.photo, function(i, item) {
var photoID = item.id;
$.getJSON('http://api.flickr.com/services/rest/?&method=flickr.photos.getInfo&api_key=' + apiKey + '&photo_id=' + photoID + '&format=json&jsoncallback=?', function(data){
if (data.photo.tags.tag != '') {
$.each(data.photo.tags.tag, function(j, item) {
tagsArr.push(item.raw);
});
}
});
tags = tagsArr.join('<br />');
console.debug(tagsArr.length);
});
$('#total-dragged').append(data.photos.total);
$('#types-dragged').append(tags);
});
});
Your calls to getJSON are asynchronous. Hence all the calls to the inner getJSON will still be outstanding by the time the console.debug line is reached. Hence the array length is still 0.
You need to run some extra code once the final getJSON call has completed.
$(function(){
var apiKey = [myapikey];
var tags = '';
var tagsArr = new Array();
$.getJSON('http://api.flickr.com/services/rest/?&method=flickr.people.getPublicPhotos&api_key=' + apiKey + '&user_id=46206266#N05&extras=date_taken,tags&format=json&jsoncallback=?', function(data){
var totalExpected = data.photos.total;
var totalFetched = 0;
$.each(data.photos.photo, function(i, item) {
var photoID = item.id;
$.getJSON('http://api.flickr.com/services/rest/?&method=flickr.photos.getInfo&api_key=' + apiKey + '&photo_id=' + photoID + '&format=json&jsoncallback=?', function(data){
if (data.photo.tags.tag != '') {
$.each(data.photo.tags.tag, function(j, item) {
tagsArr.push(item.raw);
totalFetched += 1;
if (totalFetched == totalExpected)
fetchComplete();
});
}
});
function fetchComplete()
{
tags = tagsArr.join('<br />');
console.debug(tagsArr.length);
}
});
$('#total-dragged').append(data.photos.total);
$('#types-dragged').append(tags);
});
});
This works assuming the total number of photos doesn't excede the default 100 per page, other wise you would need to tweak it.
That said I don't think using .each to fire off loads of getJSON requests makes a great deal of sense. I would refactor it so that only one call to getJSON is outstanding at any one time. Have the callback of one issue the next getJSON for the next photo until all have been pulled then do your completed code.
$.getJSON is asynchronous (the a in ajax). That means that by the time you get to console.debug(), getJSON is still getting. You'll need to do some extra work in the JSON callback.
The reason for this is that getJSON is an asynchronous request. after the call to $.getJSON, the javascript engine will move immediately on to the following two lines of code, and will output the length of your array, which is by then, zero-length. Not until after that does the getJSON request receive a response, and add items to the array.
The getJSON function is asynchronous, so when you call the debug function the array is still empty because the requests are not completed. Use the $.ajax function and set async:false and it will work.
$.ajax({
type: "GET",
url: 'http://api.flickr.com/services/rest/?&method=flickr.photos.getInfo&api_key=' + apiKey + '&photo_id=' + photoID + '&format=json&jsoncallback=?',
dataType: "json",
async:false,
success:function(data){
if (data.photo.tags.tag != '') {
$.each(data.photo.tags.tag, function(j, item) {
tagsArr.push(item.raw);
});
}
}
});
This isn't a scope issue - the problem is that getJSON is asynchronous, so it continues executing immediately after sending the request to flickr. By the time the browser executes console.debug the request hasn't returned and you haven't finished handling the response (and therefore haven't pushed any items into the array yet).
To solve this, find all the code that should only be executed when the array is full and move it into your getJSON callback method:
if (data.photo.tags.tag != '') {
$.each(data.photo.tags.tag, function(j, item) {
tagsArr.push(item.raw);
});
tags = tagsArr.join('<br />');
console.debug(tagsArr.length);
$('#total-dragged').append(data.photos.total);
$('#types-dragged').append(tags);
}
You may want to check the answer to this question I posted. There is some good information on scope issues in javascript.