Can't use the value obtained by calling getResponseHeader
-
08-06-2021 - |
Question
I'm having problems to use the value which I obtain when I call getResponseHeader
on my Greasemonkey script.
First of all, I have declared the following global variables:
size = 0;
maxS = 153600; // Max. size (bytes).
Once the website has been completely loaded, my main function is called and inside of it I get all images within a certain div (which is not shown here because it's not relevant):
function main() {
...
var imgs = posts[i].getElementsByTagName('img');
for (var j = 0; j < imgs.length; ++j) {
getFileSize(imgs[j].src, responseHandler);
if (size > maxS)
// Do something
}
...
}
And this is how getFileSize
works:
var getFileSize = function(address, responseHandler) {
var req = new XMLHttpRequest();
req.open('head', address, true);
req.onreadystatechange = responseHandler;
req.send(null);
};
var responseHandler = function(resp) {
if (this.readyState == 1)
this.abort();
size = this.getResponseHeader("Content-length");
};
The problem is that even though if I check the value of size after the sentence
size = this.getResponseHeader("Content-length");
the value is the one I expect, in the main function its value is still 0 when it reaches this part of the code:
if (size > maxS)
// Do something
I tried to figure out the answer by checking some other questions which I found in this website and I guess it has something to do with synchronization but I don't really know how to fix this issue.
Solution
The images are all on the exact same server, right? XMLHttpRequest()
will not do cross-domain requests (without some new hoops that don't apply in a Greasemonkey context, anyway).
There are a few problems with that code:
- It expects size to be set synchronously but calls
XMLHttpRequest()
asynchronously. - It aborts the request! And too soon. There is no good reason to abort a
HEAD
request. - It uses a "magic number" in an obscuring way. Use the proper enumerated constant, when available. EG
this.readyState == 1
is bad.this.readyState == this.OPENED
is better.
For synchronous operation, change getFileSize()
. Call it like so:
getFileSize (imgs[j].src);
Define it like so:
function getFileSize (address) {
var request = new XMLHttpRequest();
request.open ('HEAD', address, false); //-- false = Synchronous
request.send (null);
size = -666; // Error, or AJAX fail.
if (request.status === 200) {
size = request.getResponseHeader ("Content-length");
}
}
Notes:
- For performance and user-friendliness reasons, it would be better to refactor the approach to use asynchronous AJAX. But that's another question and less intuitive.
- If the images are cross-domain, you will need to use
GM_xmlhttpRequest()
-- which operates slightly differently and does not work properly for synchronous AJAX (last I checked -- which was a couple months ago).