I have a JS script in a file which gets the timeout value from an inline script, which I do not have the ability to change, only the JS script.
The generated HTML looks something like this, which I cannot control:
<html>
<head>
<script="script-i-have-control-over.js"></script>
window.statusTimeout="3000";
</head>
So I have this JS function in the script-i-have-control-over.js:
function getStatus() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var response = JSON.parse(this.responseText);
if (response) {
var statusValue = JSON.parse(response.data);
document.getElementById('status').innerHTML = statusValue;
}
}
};
xhttp.open("GET","/index.php?api_status",true);
xhttp.send();
setTimeout(getStatus, window.statusTimeout);
};
getStatus();
This function only works after the first execution because window.statusTimeout is undefined on first execution, but then gets the value 3000 on second execution.
I think the problem is the getStatus() call at the end is executed on page load. Further, I want the first call of getStatus() to be able to get the value of window.statusTimeout, and the setTimeout only to fire after the the 3000ms, then every 3000ms.
Currently, getStatus function is executed twice on page load, fails the first time, works the second time then continues to work every 3000ms.
Where am I going wrong? To reiterate, I do not have control over the inline JS in the HTML. I need the AJAX to do its job ASAP on page load too.
The problem appears to be that getStatus is being called as soon as the script is read, which is before window.statusTimeout is set.
This will delay the first call to getStatus until the window's load event:
window.addEventListener('load', getStatus);
That may provide enough time for window.statusTimeout="3000"; to be evaluated.
Or you could simply change this line:
setTimeout(getStatus, window.statusTimeout);
to:
setTimeout(getStatus, window.statusTimeout || 3000);
Related
Came here again with lame questions as I am in process of learning/coding.
I would like to change a property of a disable value on button: During function performing its job, button should be disabled, once function finishes and return the values, button should be enabled again.
In function which creates a buttons I am calling update() function which loading php file via XMLHttpRequest. Then running the php code and return values on page. I want to have button disabled during this time. But everytime I call the function the button will not change. Or if changed it was so fast that I didnt even saw it.
here is a code:
global_button = document.createElement("button");
// let btn1 = document.createElement("button");
global_button.innerHTML = "UPDATE";
global_button.id = "update";
global_button.disabled = false;
document.body.appendChild(global_button);
document.getElementsByClassName("two")[0].append(global_button);
global_button.addEventListener("click", function () {
console.log("After CLICKED");
global_button.disabled = true;
update();
global_button.disabled = false;
console.log("AFTER FUNCTION RETURN VALUES");
update function:
var xmlhttp;
function loadFile(file, func){
xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = func;
xmlhttp.open("GET", file, true);
xmlhttp.send();
}
function update(){
loadFile("update.php", function(){
if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
document.getElementById("content").innerHTML = xmlhttp.responseText;
}
});
}
When I checked the console, it shows both console logs immediately: "After CLICKED" and "AFTER FUNCTION RETURN VALUES" messages. And couples seconds later, result of the function appear. But button wont change whatsoever.
I am suspecting the sync/async functions ? I read something for the .open method and vale true/false, but nothing changed if I switched from true to false. Also thinking if I should put it on the loop or something which will check the button clicked ? But I thought that listener would do the job.
Can anybody check and give me an advice ? or correct my thinking if it's wrong?
many thanks all of you. :)
The problem is indeed due to the asynchronous nature of the send method of XMLHttpRequest - and therefore of your update, which calls it.
When you call update(), which itself calls this:
loadFile("update.php", function(){
if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
document.getElementById("content").innerHTML = xmlhttp.responseText;
}
});
all that happens is that you set up an XMLHttpRequest object and use its send method to send a request, telling it to call this function:
function(){
if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
document.getElementById("content").innerHTML = xmlhttp.responseText;
}
});
as a "callback" when the readyState changes. (And in particular, when the request is complete and a response received.) But calling update does not wait for that state change to happen and block your code from running - hence the next lines of code, which set the disabled state of the button to false and log to the console - are executed straight away. So the button gets disabled but then instantly un-disabled, and therefore you never see it disabled. (In fact the browser will never even "paint" the screen with a disabled button, since it doesn't get a chance to do this while your code is running, so even if you could in theory do a freeze-frame here you would never see a disabled button.)
To fix it, you have to work with the asynchronous code you're using. Anything you want to happen after the state change has to take place in the callback function you pass it. So you can simply fix your problem by changing the update definition to this:
function update(){
loadFile("update.php", function(){
if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
document.getElementById("content").innerHTML = xmlhttp.responseText;
global_button.disabled = false;
console.log("AFTER FUNCTION RETURN VALUES");
}
});
}
and delete those two lines of code from the place you've currently got them, after the update call.
Although note that this will only work if global_button is in scope inside update, which it might not be depending on how your code is structured (it probably shouldn't be to be honest). And even if it does, it's not good to hardcode your update to always undisable the button afterwards, with no guarantee the button will even be disabled first.
It's therefore better to define update to itself take a callback function:
function update(callback){
loadFile("update.php", function(){
if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
document.getElementById("content").innerHTML = xmlhttp.responseText;
callback();
}
});
}
and then call it like this in your main code:
global_button.disabled = true;
update(function() {
global_button.disabled = false;
console.log("AFTER FUNCTION RETURN VALUES");
});
because this separates the concerns of update itself ("make this request and set the content inner HTML to the response"), from whatever you might want to do afterwards, which could be different each time.
Finally, I can't not mention that this callback-based asynchronous code is very old-fashioned now. XMLHTTPRequest itself is quite a cumbersome API. I highly recommend you look into its modern equivalent, fetch, which is based on Promises - which while not without their mental gotchas are a much more understandable way to write asynchronous code. In particular with async and await you can write code that looks much like what you originally had: putting await update(); would actually do what you are waiting, and have the rest of your code wait for update to complete. But you can't just make that change to your original code because that only works if update returns a Promise, which in turns would mean completely rewriting your loadFile to use a more modern, Promise-based approach.
I know that there is similar topics, but I cannot find answer to the following question. Why The first piece of code executes callback while the second doesn't.
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function () {
console.log("Request is ready");
}
xmlHttp.open("GET", "www.google.com", true);
var result = 0;
while(1 < 5) {
}
In the code above Request is ready is printed. But with the following code:
setTimeout(function () {
console.log("Test")
}, 5)
while (1 < 5) {
}
Test is not printed. Why this is happening? I thought that onreadystatechange will push this function to be executed when the request is ready, but because we have an infinite loop the looper will not check the queue (because looper loops on the main thread and it is busy). This logic works on the second example, but not on the first. Obviously I am missing something.
Thanks in advance.
There's no asynchronous event in the first case: open triggers the first state change and synchronously calls the onreadystatechange callback.
The "Request is ready" log is done before the loop is entered.
If you add the xmlHttp.send and you call a non cached URL, you'll notice the callback isn't called for the other changes, because those ones are really asynchronous.
I'm writing a ColdFusion application that fills with some HTML content some divs once the corresponding button is clicked.
What happens is that the readyState never goes up from the initial state of 1.
The fact that makes me crazy is that I used the same AJAX code in other modules that work fine.
I tried manually the code in my applet "___AJAX_load_translator.cfm" to see if works correctly (inputting a complete url with parameters and query string) and it works.
I put many alerts in these javascript functions to trace if the url was created correctly, the parameters were formatted correctly and so on. Everything seems fine. This is driving me crazy. The result is the same on FireFox and IE.
function getHTTPObject(){
if (window.ActiveXObject) return new ActiveXObject("Microsoft.XMLHTTP");
else
if (window.XMLHttpRequest) return new XMLHttpRequest();
else {
alert("No AJAX support.");
return null;
}
}
function setOutput(divID){
if(httpObject.readyState == 4 && httpObject.status == 200){
document.getElementById(divID).innerHTML = httpObject.responseText;
} // else alert(httpObject.readyState + ' ' + httpObject.status);
}
function loadeditor(divID,CP,PP){
<CFOUTPUT>var CF_TOKENS = "CFID=#CFID#&CFTOKEN=#CFTOKEN#";</CFOUTPUT>
var operativeurl= "___AJAX_load_translator.cfm?"+CF_TOKENS+"&CP="+CP+"&PP="+PP;
httpObject = getHTTPObject();
if (httpObject != null) {
httpObject.open("POST", operativeurl, true);
httpObject.onreadystatechange = setOutput(divID);
httpObject.send(null);
}
}
I noticed that, putting an alert into the setOutput function, it displays a sudden readystate of 1. Then the browser statusbar shows the status of wait for a call to the server, that disappears quite immediately. It seems that the call is really done in that moment, and probably it is imho.
But it seems to me that after that readyness of the call (state 1) there is no more proceeding. It seems somehow blocked. Or, the function setOutput is deactivated. Maybe a second change to a state of 4 happens and this state is not registered by the callback ? In this case, why the DIV is not updated with the new content ?
Thanks for any help.
httpObject.onreadystatechange = setOutput(divID);
^^^^^^^
You're calling/executing your setouput function right then and there, and whatever the function returns becomes on the onreadystatechange callback "pointer".
Remove the (divID) portion, so you assign the function itself, not whatever it returns:
httpObject.onreadystatechange = setOutput;
For those who wants the soluce before the question :
1 ) Don't read an element with getElementById() before it's really created : see windows.onload.
2 ) If you are using XMLHTTPRequest and AJAX stuff, call you're getElementById() , or unlock this function, in the callback ,(xhr_object.readyState == 4 part of your request).
In my case, i call my page without using the desired callback (noobish Ctrl-C Ctrl-V style).
Here was the question :
I'm facing a strange case in a HTML/Javascript code.
The aim of this code is to get the value of an HTML input (type text) via a JS function.
The context is that a main HTML page loads my all my JS functions, and loads on demand HTML contents in sub divs via HTTPRequest.
The selected code is called after the divs has been loaded.
Here's the PHP generated Input field that i have to read:
<input id="listejf" type="text" value="6|7|">
Here's my JavaScript call :
listejf=document.getElementById('listejf').value;
alert(listejf);
This code doesn't work. Firebug sends me:
TypeError: document.getElementById("listejf") is null
The strange thing is that I can make it work if I call the getElementById through an alert like this:
alert(document.getElementById("listejf"));
listejf=document.getElementById('listejf').value;
alert(listejf);
The first alert displays null, but the second one "6|7|", as expected.
Now, 2 questions:
Why does alert make it work ?
How can I make it work without throwing alerts everywhere?
Re-edit, the code was gone :
That's is the main HTML page : main.html
<head>
<script type="application/javascript" src="./preload.js"></script>
</head>
<body>
Link
<div id="targetid"></div>
</body>
preload.js looks like that :
function CallPagen() {
envoieRequete('./PageN.php', 'targetid');
}
function mytestfunction() {
listejf = document.getElementById('listejf').value;
alert(listejf);
}
function envoieRequete(url, id) {
var xhr_object = null;
if (window.XMLHttpRequest) xhr_object = new XMLHttpRequest();
else if (window.ActiveXObject) xhr_object = new ActiveXObject("Microsoft.XMLHTTP");
xhr_object.open("GET", url, true);
xhr_object.onreadystatechange = function () {
if (xhr_object.readyState == 4) {
if (!document.getElementById(id)) {
alert("id pas trouvé " + id);
}
document.getElementById(id).innerHTML = xhr_object.responseText;
mytestfunction();
}
};
xhr_object.send(null);
}
PageN.php just echoes the inputtext field with the value filled.
"...I can make it work if i call the getelement through an alert like this..."
This almost always means that you're making an asynchronous AJAX request.
"Why does alert make it work ?"
What happens is that the alert delays the processing of the next line of code long enough for the response to return.
Without the alert, the next line of code runs immediately, and the element is not yet available.
"How can I make it work without throwing alerts everywhere?"
This is a very common issue. The solution is that you need to put any code that relies on the response of the XMLHttpRequest request inside a callback to the request.
So if you're making a request through the native API, you'd add a onreadystatechange callback...
xhrRequest.onreadystatechange = function() {
if (xhrRequest.readyState === 4) {
// append the response text to the DOM
listejf=document.getElementById('listejf').value;
alert(listejf);
}
}
You should run the code after DOM is ready, when the alert() is called, document is loading and browser has time for creating the DOM objects, try the following:
The load event fires at the end of the document loading process. At this point, all of the objects in the document are in the DOM, and all the images and sub-frames have finished loading.
window.onload = function() {
var listejf = document.getElementById('listejf').value;
alert(listejf);
};
With complete control over both the client and server side code, I'd like to accomplish the following:
Initiate a server request in a javascript function
Be able to abandon the request (from the user experience perspective) after a specified time
Access information about the response (e.g. either a redirect URL or part of the response body) before exiting the original function (this part is non-negotiable; setting a window interval, for example, will not cut it)
This sounds a lot like multithreading to me, which of course javascript doesn't do. Perhaps there's no solution, but I'm exhausting my options before admitting to that. In the non-working example below, function foo() sets an iframe's src to the url of a page -- redirect.aspx here -- which after a short delay redirects to another page with some UUID in the query string. (Note: it could just as well return the UUID in a hidden field in the response body, or via some other strategy; I have control over this).
Regardless how the server page returns the result, my goal is to access the UUID from the server before foo() exits.
Update: Suggested Unit Test
Though this question appears to be about scope -- and therefore solvable via closures (test pending) -- it's actually about continuity of execution. A successful test would consist of:
Create the foo() function
Assign something.onClick = foo()
foo() somehow initiates a server call and retrieves a URL from the response
foo() then calls window.open(url); using that URL
A window opens in all major browsers (critical case: IE 7, 8 & 9)
I do not currently know of a strategy that can pass this test.
Non-working sample:
<iframe id="aFrame" src="" height="0" width="0"></iframe>
<script type="text/javascript" language="javascript">
function foo() {
var f = document.getElementById("aFrame");
var loc = "http://localhost:8080/redirect.aspx?after=1000";
f.src = loc;
var start = new Date().getTime();
while (elapsedSince(start < 5000)) { // allow for server response
// FAIL: this is never true until after foo() exits:
if (f.src != loc) {
alert(encodeURIComponent(f.src));
return true;
}
}
alert("Timeout");
return false;
}
function elapsedSince(startTime) { // omitted safety checks for brevity:
return new Date().getTime() - startTime;
}
</script>
I'm not an ace at Ajax functions, but according to my understanding they require a callback, which means any return information arrives outside of the initiating function. Fail.
The above strategy doesn't work, per comments in the js code.
Other strategies might include something like using document.createElement() to create the iframe and .insertBefore() to add it to the DOM, but I'm still not certain I would be able both to initiate that and access any response details from within the same iteration of foo().
Does anyone know of any strategy that meets the above criteria?
Here is some code that would pass your "test". It gets a URL from an external page and opens a new window with that URL.
It is non-blocking as you set up a listener for readystatechange events (callback).
function foo() {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/pathname', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
window.open(xhr.responseText);
}
};
};
button.onclick = foo;