AJAX response text undefined - javascript

I am following below 2 posts:
Ajax responseText comes back as undefined
Can't return xmlhttp.responseText?
I have implemented the code in same fashion. But I am getting
undefined is not a function
wherever i am using callback() funtion in my code.
CODE:
function articleLinkClickAction(guid,callback){
var host = window.location.hostname;
var action = 'http://localhost:7070/assets/find';
var url = action + '?listOfGUID=' + guid.nodeValue;
console.log("URL "+url);
xmlhttp = getAjaxInstance();
xmlhttp.onreadystatechange = function()
{
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var response = JSON.parse(xmlhttp.responseText);
console.log(response);
console.log(xmlhttp.responseText);
callback(null, xmlhttp.responseText);// this is line causing error
}
else{
callback(xmlhttp.statusText);// this is line causing error
}
};
xmlhttp.open("GET", url, true);
xmlhttp.send(null);
}
And I am calling it from this code:
var anchors = document.getElementsByTagName("a");
var result = '';
for(var i = 0; i < anchors.length; i++) {
var anchor = anchors[i];
var guid = anchor.attributes.getNamedItem('GUID');
if(guid)
{
articleLinkClickAction(guid,function(err, response) { // pass an anonymous function
if (err) {
return "";
} else {
var res = response;
html = new EJS({url:'http://' + host + ':1010/OtherDomain/article-popup.ejs'}).render({price:res.content[i].price});
document.body.innerHTML += html;
}
});
}
}

You are using a single global variable for your xmlhttp and trying to run multiple ajax calls at the same time. As such each successive ajax call will overwrite the previous ajax object.
I'd suggest adding var in front of the xmlhttp declaration to make it a local variable in your function so each ajax request can have its own separate state.
function articleLinkClickAction(guid,callback){
var host = window.location.hostname;
var action = 'http://localhost:7070/assets/find';
var url = action + '?listOfGUID=' + guid.nodeValue;
console.log("URL "+url);
// add var in front of xmlhttp here to make it a local variable
var xmlhttp = getAjaxInstance();
xmlhttp.onreadystatechange = function()
{
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var response = JSON.parse(xmlhttp.responseText);
console.log(response);
console.log(xmlhttp.responseText);
callback(null, xmlhttp.responseText);// this is line causing error
}
else{
callback(xmlhttp.statusText);// this is line causing error
}
};
xmlhttp.open("GET", url, true);
xmlhttp.send(null);
}
In the future, you should consider using Javascript's strict mode because these "accidental" global variables are not allowed in strict mode and will report an error to make you explicitly declare all variables as local or global (whichever you intend).
I can't say if this is the only error stopping your code from working, but it is certainly a significant error that is in the way of proper operation.
Here's another significant issue. In your real code (seen in a private chat), you are using:
document.body.innerHTML += html
in the middle of the iteration of an HTMLCollection obtained like this:
var anchors = document.getElementsByTagName("a");
In this code, anchors will be a live HTMLCollection. That means it will change dynamically anytime an anchor element is added or removed from the document. But, each time you do document.body.innerHTML += html that recreates the entire body elements from scratch and thus completely changes the anchors HTMLCollection. Doing document.body.innerHTML += html in the first place is just a bad practice. Instead, you should append new elements to the existing DOM. I don't know exactly what's in that html, but you should probably just create a div, put the HTML in it and append the div like this:
var div = document.createElement("div");
div.innerHTML = html;
document.body.appendChild(div);
But, this isn't quite all yet because if this new HTML contains more <a> tags, then your live HTMLCollection in anchors will still change.
I'd suggestion changing this code block:
var anchors = document.getElementsByTagName("a");
var result = '';
for(var i = 0; i < anchors.length; i++) {
var anchor = anchors[i];
var guid = anchor.attributes.getNamedItem('GUID');
if(guid)
{
articleLinkClickAction(guid,function(err, response) { // pass an anonymous function
if (err) {
return "";
} else {
var res = response;
html = new EJS({url:'http://' + host + ':1010/OtherDomain/article-popup.ejs'}).render({price:res.content[i].price});
document.body.innerHTML += html;
}
});
}
}
to this:
(function() {
// get static copy of anchors that won't change as document is modified
var anchors = Array.prototype.slice.call(document.getElementsByTagName("a"));
var result = '';
for (var i = 0; i < anchors.length; i++) {
var anchor = anchors[i];
var guid = anchor.attributes.getNamedItem('GUID');
if (guid) {
articleLinkClickAction(guid, function (err, response) { // pass an anonymous function
if (err) {
//return "";
console.log('error : ' + err);
} else {
var res = response;
var html = new EJS({
url: 'http://' + host + ':1010/OtherDomain/article-popup.ejs'
}).render({
price: res.content[i].price
});
var div = document.createElement("div");
div.innerHTML = html;
document.body.appendChild(html);
}
});
}
}
})();
This makes the following changes:
Encloses the code in an IIFE (self executing function) so the variables declared in the code block are not global.
Changes from document.body.innerHTML += html to use document.body.appendChild() to avoid recreating all the DOM elements every time.
Declares var html so it's a local variable, not another accidental global.
Makes a copy of the result from document.getElementsByTagName("a") using Array.prototype.slice.call() so the array will not change as the document is modified, allowing us to accurately iterate it.

Related

XMLHttpRequest in for loop

I am trying to make several server requests inside a for loop. I found this question and implemented the suggested solution. However it doesn't seem to work.
for (var i = 1; i <= 10; i++)
{
(function(i) {
if(<some conditions>)
{
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp[i]=new XMLHttpRequest();
} else { // code for IE6, IE5
xmlhttp[i]=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp[i].onreadystatechange=function() {
if (xmlhttp[i].readyState==4 && xmlhttp[i].status==200) {
document.getElementById("preselection").innerHTML=xmlhttp[i].responseText;
}
}
xmlhttp[i].open("GET","getBuoys.php?q="+i,true);
xmlhttp[i].send();
}
})(i);
}
If I remove the for loop and change all xmlhttp[i] to xmlhttp, everything works just fine for one element, but I can't make several requests. Thanks in advance for any suggestions.
Try the snippet below
// JavaScript
window.onload = function(){
var f = (function(){
var xhr = [], i;
for(i = 0; i < 3; i++){ //for loop
(function(i){
xhr[i] = new XMLHttpRequest();
url = "closure.php?data=" + i;
xhr[i].open("GET", url, true);
xhr[i].onreadystatechange = function(){
if (xhr[i].readyState === 4 && xhr[i].status === 200){
console.log('Response from request ' + i + ' [ ' + xhr[i].responseText + ']');
}
};
xhr[i].send();
})(i);
}
})();
};
// PHP [closure.php]
echo "Hello Kitty -> " . $_GET["data"];
Response
Response from request 0 [ Hello Kitty -> 0]
Response from request 1 [ Hello Kitty -> 1]
Response from request 2 [ Hello Kitty -> 2]
First thing first, that's awful formatting. A small request to keep it a bit more parseable in future please.
We can clean this up though.
var XMLHttpRequest
= XMLHttpRequest || require('xmlhttprequest').XMLHttpRequest;
// Makes a request for 4 buoy page responses.
requestAllBuoys(4, function(requests) {
console.log('Got results!');
// Take out the responses, they are collected in the order they were
// requested.
responses = requests.map(function(request) {
return request.responseText;
});
// Left to you to implement- I don't know what you're going to do with
// your page!
updateDom(responses);
});
// Makes request to all buoy url's, calling the given callback once
// all have completed with an array of xmlRequests.
function requestAllBuoys (n, cb) {
var latch = makeLatch(n, cb);
makeBuoyURLTo(n).map(function (url, i) {
startXMLRequest('GET', url, latch.bind(undefined, i));
});
}
// Generates a latch function, that will execute the given callback
// only once the function it returns has been called n times.
function makeLatch (n, cb) {
var remaining = n,
results = [],
countDown;
countDown = function (i, result) {
results[i] = result;
if (--remaining == 0 && typeof cb == 'function') {
cb(results);
}
}
return countDown;
}
// Generates an array of buoy URL's from 1 to n.
function makeBuoyURLTo (n) {
var i, buoyUrls = [];
for (i = 1; i <= n; i++) {
buoyUrls.push('getBuoys.php?q=' + i);
}
return buoyUrls;
}
// Create and initiate an XMLRequest, with the given method to the given url.
// The optional callback will be called on successful completion.
function startXMLRequest (method, url, cb) {
var xmlRequest = createXMLRequest();
xmlRequest.onreadystatechange = function () {
if (isXMLFinished(xmlRequest)) {
if (cb && typeof cb == 'function') {
cb(xmlRequest, method, url);
}
}
}
xmlRequest.open(method, url, true);
xmlRequest.send();
return xmlRequest;
}
// Initiates an XMLRequest from either HTML5 native, or MS ActiveX depending
// on what is available.
function createXMLRequest () {
var xmlRequest;
if (XMLHttpRequest) {
xmlRequest = new XMLHttpRequest();
} else {
xmlRequest = new ActiveXObject('Microsoft.XMLHTTP');
}
return xmlRequest;
}
// Verifies that XMLRequest has finished, with a status 200 (OK).
function isXMLFinished (xmlRequest) {
return (xmlRequest.readyState == 4) && (xmlRequest.status == 200);
}
This may seem longer, but it makes things infinitely clearer, and the time you spent making it so is time you don't spend debugging.
It also allows you to access the final result together, in the order that they came as a standard array. This is the main added bulk.
I would say you have a good idea of what you're actually doing here, as to me the only thing about your code that wouldn't work is the updating of the dom (surely you'll just be assigning them rapidly all into the same element? replacing each other each time...).
Have a look at this answer about handling async callbacks if you're still struggling. But please, for your own sake, keep your code cleaner.

Issue with for-loop and standard Javascript AJAX

I have some issues with a for-loop and AJAX. I need to fetch some information from a database, so I pass the incrementing variable to PHP to grab the information and then send it back. The trouble is that it skips immediately to the maximum value, making it impossible to store any of the information.
I would prefer not to use jQuery. It may be more powerful, but I find Javascript easier to understand.
Here is the JS code:
for (var i = 0; i <= 3; i++) {
var js_var = i;
document.getElementById("link").onclick = function () {
// ajax start
var xhr;
if (window.XMLHttpRequest) xhr = new XMLHttpRequest(); // all browsers
else xhr = new ActiveXObject("Microsoft.XMLHTTP"); // for IE
var url = 'process.php?js_var=' + js_var;
xhr.open('GET', url, false);
xhr.onreadystatechange = function () {
if (xhr.readyState===4 && xhr.status===200) {
var div = document.getElementById('test1');
div.innerHTML = xhr.responseText;
if (js_var == 2) {
var rawr = document.getElementById('test2');
rawr.innerHTML = xhr.responseText;
}
}
}
xhr.send();
// ajax stop
return false;
}
};
Here is the PHP code:
<?php
if (isset($_GET['js_var'])) $count = $_GET['js_var'];
else $count = "<br />js_var is not set!";
$con = mysql_connect("xxx","xxxxx","xxxx");
mysql_select_db('computerparty_d', $con);
$get_hs = mysql_query("SELECT * FROM hearthstone");
$spiller_navn = utf8_encode(mysql_result($get_hs,$count,1));
echo "$spiller_navn";
?>
what you actually are doing is binding an onclick event in your for-loop not sending ajax request, and the other point is, it immediately overrides the previous onclick handler which you have created in the previous iteration.
So if you want to add multiple listeners you should first consider using nested functions and closures to keep the i variable safe for each listener, and then use addEventListener instead of setting the onclick function. Considering these points you can do this instead:
for (var i = 0; i <= 3; i++) {
var clickFunc = (function (js_var) {
return function () {
// ajax start
var xhr;
if (window.XMLHttpRequest) xhr = new XMLHttpRequest(); // all browsers
else xhr = new ActiveXObject("Microsoft.XMLHTTP"); // for IE
var url = 'process.php?js_var=' + js_var;
xhr.open('GET', url, false);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var div = document.getElementById('test1');
div.innerHTML = xhr.responseText;
if (js_var == 2) {
var rawr = document.getElementById('test2');
rawr.innerHTML = xhr.responseText;
}
}
}
xhr.send();
// ajax stop
return false;
};
})(i);
document.getElementById("link").addEventListener("click", clickFunc);
}
Be aware that you're making an synchronous AJAX call, which is undesirable (it hangs the browser during the request, which might not end). You may have problems in some browsers with this because you're calling onreadystatechange, that shouldn't be used with synchronous requests.
It looks like you are making the AJAX request with a user click.
for (var i = 0; i <= 3; i++) {
var js_var = i;
document.getElementById("link").onclick
When this JS is executed it will override the "onclick" listener of "link" twice. First time it is assigned for the first time, second time it is overwritten, and the third time it is overwritten again. The result is that when the "link" element is clicked only the last listener exists, resulting in making a single AJAX request for the last configuration.
HTTP request are expensive(time), it might be worth to get all of the data in one request and then use client-side JS to sift through that data accordingly.
jQuery is not more powerful than JS, it is JS with a bunch of wrapper functions. My personal opinion is that once IE9 is no longer relevant, jQuery will be only used by people who know jQuery and not JS.

Javascript: XHR not handling multiple async requests

Hi I am trying to access one resource multiple times with with different parameters
In this case requesting
var domains = [
'host1',
'host2'
];
var requests = new Array();
for ( i in domains )
{
requests[i]=new request(domains[i]);
}
function request(site)
{
var url = 'get_remote_status.php?host='+site;
var queues = {};
http_request = new XMLHttpRequest();
http_request.open("GET", url, true, 'username', 'password');
http_request.onreadystatechange = function () {
var done = 4, ok = 200;
if (http_request.readyState == done && http_request.status == ok) {
queues = JSON.parse(http_request.responseText);
var queuesDiv = document.getElementById('queues');
print_queues(queues, queuesDiv, site);
}
};
http_request.send(null);
}
However, only one of of the requests is being handled by the code lambda. Chromium reports that both requests have been received and is viewable in the resourced pane.
Also if I make the request synchronous then it works fine. However this is not acceptable to the release code as a request may timeout.
Thanks
Define http_request using var. Currently, you're assigning the XHR object to a global variable. Because of this, your script can only handle one XHR at a time.
Relevant erroneous code:
function request(site)
{
var url = 'get_remote_status.php?host='+site;
var queues = {};
http_request = new XMLHttpRequest();
Proposed change:
function request(site)
{
var url = 'get_remote_status.php?host='+site;
var queues = {};
var http_request = new XMLHttpRequest(); //VAR VAR VAR !!!
When you omit var before a variable, the variable will be defined in the global (window) scope. If you use var before a variable, the variable is defined within the local scope (in function request, in this case).
In fact it is possible to run multiple async xhr call but you have to give them an unique id as parameter to be able to store and load them locally in your DOM.
For example, you'd like to loop on an array and make a ajax call for each object. It's a little bit tricky but this code works for me.
var xhrarray={};
for (var j=0; j<itemsvals.length; j++){
var labelval=itemsvals[j];
// call ajax list if present.
if(typeof labelval.mkdajaxlink != 'undefined'){
var divlabelvalue = '<div id="' + labelval.mkdid + '_' + item.mkdcck + '" class="mkditemvalue col-xs-12 ' + labelval.mkdclass + '"><div class="mkdlabel">' + labelval.mkdlabel + ' :</div><div id="'+ j +'_link_'+ labelval.mkdid +'" class="mkdvalue">'+labelval.mkdvalue+'</div></div>';
mkdwrapper.find('#' + item.mkdcck + ' .mkdinstadivbody').append(divlabelvalue);
xhrarray['xhr_'+item.mkdcck] = new XMLHttpRequest();
xhrarray['xhr_'+item.mkdcck].uniqueid=''+ j +'_link_'+ labelval.mkdid +'';
console.log(xhrarray['xhr_'+item.mkdcck].uniqueid);
xhrarray['xhr_'+item.mkdcck].open('POST', labelval.mkdajaxlink);
xhrarray['xhr_'+item.mkdcck].send();
console.log('data sent');
xhrarray['xhr_'+item.mkdcck].onreadystatechange=function() {
if (this.readyState == 4) {
console.log(''+this.uniqueid);
document.getElementById(''+this.uniqueid).innerHTML = this.responseText;
}
};
}
}
You have to set each xhr object in a global variable object and define a value xhrarray['xhr_'+item.mkdcck].uniqueid
to get its unique id and load its result where you want.
Hope that will help you in the future.

Ajax multiple drop down list

I have 5 drop down lists which is dynamic in nature. But the only problem is all the option values are being fetched from mysql database and i really want the user to know that the query is happening at the backend and he should wait by displaying a gif or a line saying "loading.. " .
I've been looking all over for this and similar questions have been posted by others but i don't seem to get it working . Please help me out. Can somebody please give a easy solution?
Thanks.
I've placed an example here:
http://jsfiddle.net/cMEaM/embedded/result/
I've kept as much of the existing code the same so you should still recognise it. The getXMLHTTP function is the same:
function getXMLHTTP() {
//function to return the xml http object
var xmlhttp = false;
try {
xmlhttp = new XMLHttpRequest();
} catch (e) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e1) {
xmlhttp = false;
}
}
}
return xmlhttp;
}
There's a new sendGet function to handle the XHR request, which takes success and error callbacks.
function sendGet(url, onSuccess, onError) {
var req = getXMLHTTP();
var method = "GET";
if (req) {
req.onreadystatechange = function() {
if (req.readyState == 4) {
// only if "OK"
if (req.status == 200) {
onSuccess(req);
} else {
onError(req);
}
}
}
req.open(method, url, true);
req.send(data);
}
}
I borrow a throbber from Wikipedia to display when the data is loading.
var throbberHtml = "<img src='http://upload.wikimedia.org/wikipedia/en/2/29/Throbber-Loadinfo-292929-ffffff.gif'>";
And these are the new getXXX functions which replace the <select> with the throbber while the data is loading:
function getState(countryId) {
var div = document.getElementById('statediv');
var oldInnerHTML = div.innerHTML;
var onSuccess = function(req) {
div.innerHTML = req.responseText;
};
var onError = function(req) {
div.innerHTML = oldInnerHTML;
alert("There was a problem while using XMLHTTP:\n" + req.statusText);
};
div.innerHTML = throbberHtml;
sendGet("findState.php?country=" + countryId, onSuccess, onError);
}
function getCity(countryId, stateId) {
var div = document.getElementById('citydiv');
var oldInnerHTML = div.innerHTML;
var onSuccess = function(req) {
div.innerHTML = req.responseText;
};
var onError = function(req) {
div.innerHTML = oldInnerHTML;
alert("There was a problem while using XMLHTTP:\n" + req.statusText);
};
div.innerHTML = throbberHtml;
sendGet("findCity.php?country=" + countryId + "&state=" + stateId,
onSuccess, onError);
}
There are other improvements that could be made, but I tried to keep in the sprit of your existing code as much as possible.
E.g. you can see that most of code in the getXXX functions is the same, so you could refactor these to use mostly the same code. Also, using a JS framework such as jQuery will replace the XHR code with better, more cross-browser compatible code. It's usually always better to avoid reinventing the wheel when it comes to code!
And you may possibly decide that sending the HTML for a <select> tag is not the best data format for the XHR. You might go with JSON which would decouple your presentation from the data.

Why wont this JS code work if its all on the same line?

I'm writing HTML code for a Java servlet. I first write the code in html/js so I can debug what I'm working on, and then I'll make it a Java string and put it in my servlet. My problem is that the code is working fine when I view it in Firefox from a local html file, but when I view it on my Java servlet, it doesn't work because the js isn't getting called.
What I did was format the html that my servlet generated so that its not all on a single line and ran the code again. This time it worked. I copied this working code into a browser address bar so that it will all be on a single line, and copied that code back into the script in my html file. Now, when the previously working code is on a single line, it doesn't work.
Here's the formatted JS:
var sMax
var holder;
var preSet;
var rated;
var request;
function rating(num){
sMax = 0;
for(n=0; n<num.parentNode.childNodes.length; n++){
if(num.parentNode.childNodes[n].nodeName == "A"){
sMax++;
}
}
if(!rated){
s = num.id.replace("_", '');
a = 0;
for(i=1; i<=sMax; i++){
if(i<=s){
document.getElementById("_"+i).className = "on";
document.getElementById("rateStatus").innerHTML = num.title;
holder = a+1;
a++;
}else{
document.getElementById("_"+i).className = "";
}
}
}
}
function off(me){
if(!rated){
if(!preSet){
for(i=1; i<=sMax; i++){
document.getElementById("_"+i).className = "";
document.getElementById("rateStatus").innerHTML = me.parentNode.title;
}
}else{
rating(preSet);
document.getElementById("rateStatus").innerHTML = document.getElementById("ratingSaved").innerHTML;
}
}
}
function rateIt(me){
if(!rated){
document.getElementById("rateStatus").innerHTML = document.getElementById("ratingSaved").innerHTML + " "+me.title;
preSet = me;
rated=1;
sendRate(me);
rating(me);
}
}
function sendRate(sel){
alert("Your rating was: "+sel.title);
addRating("rating", "?truck=kogibbq?rating="+ sel.id);
}
function addRating(servletName, servletArguments){
var servlet = servletName;
var arg = servletArguments
var req = servlet + arg;
alert(req);
addrequest(req);
request.onreadystatechange = function(){
alert("response received");
}
}
function addrequest(req) {
try {
request = new XMLHttpRequest();
}catch (e) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
}catch (e) {
alert("XMLHttpRequest error: " + e);
}
}
request.open("GET", element, true);
request.send(null);
return request;
}
You are missing semi-colons on:
var sMax
and
var arg = servletArguments
Sarfraz has already pointed out the deal-breakers in this code, but for similar problems in the future, I recommend pasting your code into JSLint, for validation.
It'll find a lot of errors that won't actually break your code (but then neither will the lack of semicolons unless you put the entire script in one line), so you don't need to fix every single remark just to get it working, but of course, if you can follow the exact JSLint recommendations, that's usually just great.
There's a ; missing in the first line of your javascript file.

Categories