XmlHttpRequest.onload not called - javascript

I'm playing around with this XmlHttpRequest thing. In some tutorials and books, it is the onload function the one that is called when the request is done. In my little experiment, this function is never called. Here's my code:
window.onload = function() {
var url = "http://www.google.com";
var request = new XMLHttpRequest();
request.onload = function() {
var state = this.readyState;
var responseCode = request.status;
console.log("request.onload called. readyState: " + state + "; status: " + responseCode);
if (state == this.DONE && responseCode == 200) {
var responseData = this.responseText;
alert("Success: " + responseData.length + " chars received.");
}
};
request.error = function(e) {
console.log("request.error called. Error: " + e);
};
request.onreadystatechange = function(){
console.log("request.onreadystatechange called. readyState: " + this.readyState);
};
request.open("GET", url);
request.send(null);
};
I'm testing this on the last Firefox release (just updated today). The log line in onload is never printed, and the breakpoint I set in the first line is never hit. However, the onreadystatechange function is called twice, and the http request is actually made. This is what firebug's console shows:
request.onreadystatechange called. readyState: 1
GET http://www.google.com/ 302 Found 174ms
request.onreadystatechange called. readyState: 4
NS_ERROR_FAILURE: Failure
request.send(null);
There's an error in the send line. I've tried changing it to request.send() with identical result.
At first I thought this might be the browser trying to prevent XSS, so I moved my html driver page to a Tomcat instance in my dev machine, but the result is the same.
Is this function guaranteed to be called? As I've said above, it's common to be seen in most tutorials, but on the other hand in the W3C spec page, the hello world snippet uses onreadystatechange instead:
function processData(data) {
// taking care of data
}
function handler() {
if(this.readyState == this.DONE) {
if(this.status == 200 &&
this.responseXML != null &&
this.responseXML.getElementById('test').textContent) {
// success!
processData(this.responseXML.getElementById('test').textContent);
return;
}
// something went wrong
processData(null);
}
}
var client = new XMLHttpRequest();
client.onreadystatechange = handler;
client.open("GET", "unicorn.xml");
client.send();
It checks readyState == this.DONE. DONE has the value 4, which is what I can see in my log. So if this were a XSS related issue, and the browser were preventing me to make the connection to a different domain, then why the actual connection is made and the DONE status is received???
PS: Yes, I know there are powerful libraries to do this easily, but I'm still a JavaScript noob so I'd like to understand the low level first.
UPDATE:
I've changed the URL to one inside my domain (localhost), and the error is gone but the onload function is still not being called. Tested in IE8 and does not work. Tested in Chrome and works. How's that?
UPDATE 2:
Tested again in Firefox, and now it works. Probably the old page was still cached so that's why I couldn't notice it immediatly. Still failing in IE8, I'll try to test it in a newer version.

It looks like it was indeed a XSS issue and Firefox was blocking the onload call. I can't still understand why the http network request was actually being done and the onreadystatechange was being called with the DONE readyState.
I changed the URL to another one in the same domain, and now it works in Firefox (after some cache-related false attempts) and in Chrome. It still does not work in IE8, despite the official docs say it is supported. I've found this SO answer which states otherwise. It looks like the onload function is a more modern convenience method and the old way of checking the result is using onreadystatechange instead.
I guess I'll accept this answer as the solution unless a more detailed answer is provided.

The onload handler won't be called for yet another reason, I'm adding it here just so it can be helpful to someone else referencing this page.
If the HTTP response is malformed, the onload handler will not be called either. For example, a plaintext response of 10 bytes that advertises a length of 14 in Content-Length header will not invoke the onload handler. I wasted hours on client code before I start to replace back-end units with test stubs.

IE has different method to create xmlhttprequest.
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// Check if the XMLHttpRequest object has a "withCredentials" property.
// "withCredentials" only exists on XMLHTTPRequest2 objects.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// Otherwise, check if XDomainRequest.
// XDomainRequest only exists in IE, and is IE's way of making CORS requests.
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// Otherwise, CORS is not supported by the browser.
xhr = null;
}
return xhr;
};
same this article:https://www.html5rocks.com/en/tutorials/cors/

Related

XMLHttpRequest is deprecated. What to use instead?

Trying to use a pure JS approach to check if I have a valid JS image url. I am getting a warning that XMLHttpRequest is deprecated. What is a better way to do this?
urlExists(url) {
const http = new XMLHttpRequest();
http.open('HEAD', url, false);
http.send();
if (http.status !== 404) {
return true;
}
return false;
}
You're probably getting a message that the synchronous use of XMLHttpRequest is deprecated (because of its harmful effect on the user experience; it freezes the page while waiting for a response). I can assure you that proper asynchronous use of that API is not deprecated whatsoever.
Here's some example code for the correct use:
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function() {
if (this.readyState === this.DONE) {
console.log(this.status) // do something; the request has completed
}
}
xhr.open("HEAD", "http://example.com") // replace with URL of your choosing
xhr.send()
The cause of the warning was that in http.open('HEAD', url, false); you put a third argument (async) as false. As per the https://xhr.spec.whatwg.org/#synchronous-flag it should be set to true.
The warning is probably because you are tyring to do a synchronous request.

QML - XMLHttpRequest User-Agent change

I'm trying to change User-Agent used by XMLHttpRequest.
This is my connect() function.
It works perfectly but User-Agent isn't being changed..
Someone knows why? (I've checked headers with Fiddler) :(
function connect() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) {
console.log('Trying to connect...')
} else if(xhr.readyState === XMLHttpRequest.DONE) {
console.log('DONE')
}
}
xhr.open("GET", "http://www.mywebsite/index.php?waddawda");
xhr.setRequestHeader('User-Agent','FAKE-USER-AGENT');
xhr.send();
}
Thanks for your help.
P.S. If it's not possible to do it with XMLHttpRequest please suggest me an alternative :) I need to do a GET request with a modified User-Agent.
It didn't work, because back then, setting the user agent was forbidden by specification. Nowadays it should work, because it has been allowed. It works in Firefox.
In some browsers (Chrome) it still (2023-02) doesn't work because of a long standing bug.

XMLHTTPRequest onreadystatechange not called in Firefox

I have checked everywhere on all other questions to do with this and I can still not find an answer that works so I am posting this here.
Here is my code:
var link;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
link = xhr.responseText.split("::::")[0];
alert(link);
}
}
}
var url = "http://youtube.thegoblin.net/banner/getImage.php?name=" + actualName;
xhr.open("GET", url, true);
xhr.send();
Now, this works absolutely fine in Chrome, but not in Firefox. I know that the problem is not to do with onreadystatechange not being used with synchronous HTTPRequests because this is asynchronous, and it works fine in Chrome. I have tried it as a synchronous request, but anything after xhr.send(); does not run and I do not know why.
Why does this work in Chrome and not Firefox? And how can I get this to work in Firefox,
thanks.
Update
I believe the problem is becasue the request is cross domain. How can I get past this?

using javascript to detect whether the url exists before display in iframe

I'm using javascript to pass a dynamic url to iframe src. but sometimes the url does not exist, how could i detect the non-exist url beforehand, so that i can hide the iframe that with 404 error.
Due to my low reputation I couldn't comment on Derek 朕會功夫's answer.
I've tried that code as it is and it didn't work well. There are three issues on Derek 朕會功夫's code.
The first is that the time to async send the request and change its property 'status' is slower than to execute the next expression - if(request.status === "404"). So the request.status will eventually, due to internet band, remain on status 0 (zero), and it won't achieve the code right below if. To fix that is easy: change 'true' to 'false' on method open of the ajax request. This will cause a brief (or not so) block on your code (due to synchronous call), but will change the status of the request before reaching the test on if.
The second is that the status is an integer. Using '===' javascript comparison operator you're trying to compare if the left side object is identical to one on the right side. To make this work there are two ways:
Remove the quotes that surrounds 404, making it an integer;
Use the javascript's operator '==' so you will be testing if the two objects are similar.
The third is that the object XMLHttpRequest only works on newer browsers (Firefox, Chrome and IE7+). If you want that snippet to work on all browsers you have to do in the way W3Schools suggests: w3schools ajax
The code that really worked for me was:
var request;
if(window.XMLHttpRequest)
request = new XMLHttpRequest();
else
request = new ActiveXObject("Microsoft.XMLHTTP");
request.open('GET', 'http://www.mozilla.org', false);
request.send(); // there will be a 'pause' here until the response to come.
// the object request will be actually modified
if (request.status === 404) {
alert("The page you are trying to reach is not available.");
}
Use a XHR and see if it responds you a 404 or not.
var request = new XMLHttpRequest();
request.open('GET', 'http://www.mozilla.org', true);
request.onreadystatechange = function(){
if (request.readyState === 4){
if (request.status === 404) {
alert("Oh no, it does not exist!");
}
}
};
request.send();
But notice that it will only work on the same origin. For another host, you will have to use a server-side language to do that, which you will have to figure it out by yourself.
I found this worked in my scenario.
The jqXHR.success(), jqXHR.error(), and jqXHR.complete() callback methods introduced in jQuery 1.5 are deprecated as of jQuery 1.8. To prepare your code for their eventual removal, use jqXHR.done(), jqXHR.fail(), and jqXHR.always() instead.
$.get("urlToCheck.com").done(function () {
alert("success");
}).fail(function () {
alert("failed.");
});
I created this method, it is ideal because it aborts the connection without downloading it in its entirety, ideal for checking if videos or large images exist, decreasing the response time and the need to download the entire file
// if-url-exist.js v1
function ifUrlExist(url, callback) {
let request = new XMLHttpRequest;
request.open('GET', url, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.setRequestHeader('Accept', '*/*');
request.onprogress = function(event) {
let status = event.target.status;
let statusFirstNumber = (status).toString()[0];
switch (statusFirstNumber) {
case '2':
request.abort();
return callback(true);
default:
request.abort();
return callback(false);
};
};
request.send('');
};
Example of use:
ifUrlExist(url, function(exists) {
console.log(exists);
});
You could test the url via AJAX and read the status code - that is if the URL is in the same domain.
If it's a remote domain, you could have a server script on your own domain check out a remote URL.
Using async/await, this worked for me for opening a new tab; I needed to detect a 404 for the same reason as the OP:
openHelp : async function(iPossiblyBogusURL) {
const defaultURL = `http://guaranteedToWork.xyz`;
const response = await fetch(iPossiblyBogusURL);
if (response.status == 200) {
window.open(iPossiblyBogusURL, `_blank`);
} else if (response.status === 404) {
window.open(defaultURL, `_blank`);
}
},
You can try and do a simple GET on the page, if you get a 200 back it means the page exists. Try this (using jQuery), the function is the success callback function on a successful page load. Note this will only work on sites within your domain to prevent XSS. Other domains will have to be handled server side
$.get(
yourURL,
function(data, textStatus, jqXHR) {
//load the iframe here...
}
);
There is no need to make a separate HTTP request to check beforehand.
You could switch the logic around: only display the iframe if it has been loaded successfully. For this purpose, you can attach an onload event listener to the iframe.
See this related question for details: Capture iframe load complete event

xmlhttp request has strange behavior and has status = 0

I have a Google Chrome extension that upon pressing a button executes an xmlhttp request to a server. I'm currently handling the error if the server is down. What I want to achieve is to retry the request after an increasing number of seconds. That's how I do it:
var execute = function(method, url, i){
//if timeout doesn't exist, create one
if(!i){
i = 1000;
}
var xmlhttp = new XMLHttpRequest();
xmlhttp.open(method, url, true);
xmlhttp.onreadystatechange = function(){
if (xmlhttp.status != 200) {
// Handle error, retry request
console.log("xmlhttp.status = " + xmlhttp.status + " and xmlhttp.readyState = " + xmlhttp.readyState);
setTimeout("execute('"+method+"', '"+url+"', "+i*2+")", i);
return;
}
};
xmlhttp.send(null);
}
Basically if there is some kind of problem I retry again the request. If the server is up, there is no problem at all, but if the server is down JavaScript throws me an error saying:
PUT http://localhost:3000/buy/2/id=1329664820124
executepopup.html:127
(anonymous function)
Which is not really meaningful, but however. The status in the log says "0", which is pretty lame too. If I turn on the server again while this is going on, it should stop doing this, but instead also if it succeeds (I see a log in the server that tells me that it received the request), it keeps calling the execute method. I don't know how to stop this recursion if the server turns on. Am I doing something wrong? Is this state equal zero the problem?
Thanks a lot
Solving the problem
onreadystatechange is triggered for every state change, from state 0 to 4, and many times between. You should only be interested in readyState 4, because the request has fully finished at that point.
To get your method to work, check whether xmlhttp.readyState == 4:
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status != 200) {
Fixing your horrible implementation of setTimeout
setTimeout("execute('"+method+"', '"+url+"', "+i*2+")", i); is not the right way to call a function again. Since you're developing in a Chrome extension, you can use the following format for setTimeout:
setTimeout(execute, i, method, url, i*2);
// Calls execute(method, url, i*2) after i milliseconds.

Categories