I have a button as follows that I programmatically click.
document.getElementById("generateButton").click();
This button creates a table and loads the data from the database.
I then have options to sort the table that if specified in the url, the table will be sorted once loaded.
if(COL_ONE == "true")
sortTable("COL_ONE");
My problem is that the table takes around 3 seconds to load. Therefore, the if statement where the sorting happens, ends up running before the table is created. Therefore, the sorting does not occur. If I place a break point at the if statement and stop execution at the if statement, and wait until I see the table load, then run the if statement (through the debugger) then my sorting does work. Therefore, the code is all working, its just a matter of figuring out a way to stall until the table is loaded. Is there anyway I can do this, where until the table is loaded, then run the next if statement, I am doing this on page load.
You can make use of callback in this case
function tableLoaded(callback) {
//Code to load the table
callback();
}
In your main function
tableLoaded(function(){
//have your if statement
if(COL_ONE == "true")
sortTable("COL_ONE");
});
Please post the relevant DB code.
I can only guess, though, that it looks similar to this:
document.getElementById("generateButton").addEventListener('click', function() {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if ( xmlHttp.readyState == 4 && xmlHttp.status == 200 ) {
updateTable(xmlHttp.responseText);
}
};
xmlHttp.open("GET", '/get-info-from-db', true);
xmlHttp.send();
});
In which case, it is a simple change:
document.getElementById("generateButton").addEventListener('click', function() {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if ( xmlHttp.readyState == 4 && xmlHttp.status == 200 ) {
updateTable(xmlHttp.responseText);
if(COL_ONE == "true")
sortTable("COL_ONE");
}
};
xmlHttp.open("GET", '/get-info-from-db', true);
xmlHttp.send();
});
You absolutely must put the sorting functionality in a callback and set it up to be called after the data loads. I noticed you might be loading the data into the tables asynchronously, i.e. loading data in a callback function, which is probably why the sorting function is kicking in before the load.. don't do that, have the sorting to excute right after call the function to load data.. Prehaps it would be better if you told me how you are loading the data so i can tell you extactly when and where to place to the callback function with the sorting functionality. For example, if you are using third-party support to load the data, you should probably try to find in the api a function call to load data that also accepts a callback, or completion handler. For the callback, you must pass your sorting functionality in the form of an anonymous function. Hope this helps!!
I am giving my first steps in Javascript and trying to understand how it works.
I've come to a problem of execution order of the code.
var Parsed = [[]]
var txtFile = new XMLHttpRequest();
alert("Trying to open file!");
txtFile.open("GET", "http://foo/f2/statistics/nServsDistrito.txt", false);
txtFile.onreadystatechange = function() {
if (txtFile.readyState === 4) { // Makes sure the document is ready to parse.
if (txtFile.status === 200) { // Makes sure it's found the file.
alert("File Open");
allText = txtFile.responseText;
Parsed = CSVToArray(allText, ",")
}
}
}
txtFile.send(null);
alert("Job Done");
The problem is "Job Done" is appearing first than "File Open".
But the file has information necessary for code following the "Job Done" alert.
I changed the asynchronous part of the "get" request but didn't work.
What can i do to stand by all code while the file is open and the information retrieved?
Can i use the readyState to stall the code while the file is being opened and parsed?
Thanks for the help.
Update: It now works thanks to all.
XMLHttpRequest is an async operation. It doesn't matter whether your file is readily available or even if there is no networking involved. Because it is an async operation, it will always execute after after any sequential/synchronous code. That's why you have to declare a callback function (onreadystatechange) that will be called when open comes back with the file contents.
By the explanation above, your code in this example wouldn't be correct. The alert line will be executed immediately, not waiting for the file contents to be ready. The job will only be done when onreadystatechange has finished executing, so you would have to put the alert at the end of onreadystatechange.
Another very common way to trigger async operations is by using setTimeout, which forces its callback function to be executed asynchronously. Check out how it works here.
Edit: You are indeed forcing the request to be synchronous by setting the third parameter to open to false (https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#open()). There are very few cases in which you want a request like that to be synchronous, though. Consider whether you need it to be synchronous, because you will be blocking your whole application or website until the file has been read.
That's because you are using asynchronous functions. When working with async functions you have to use callbacks.
A callback is a function (eg. function cback()) you pass as parameter to another function (eg function async()). Well, cback will be used by async when necessary.
For example, if you are doing IO operations like reading files or executing SQL queries, the callback can be used to handle the data once retrieved:
asyncOperation("SELECT * FROM stackoverflow.unicorns", function(unicorns) {
for(var i=0; i<unicorns.length; i++) {
alert("Unicorn! "+unicorns[i].name);
}
});
The anonymous function we are giving asyncOperation as the second parameter is the "callback", and it's going to be executed once the query data is ready. But while that operation is being handled your script is not blocked, this means that if we add this line after the previous code:
alert("We are not blocked muahahaha");
That alert will be shown before the query is completed and unicorns appear.
So, if you want to do something after the async task finishes, add that code inside the callback:
asyncOperation("SELECT * FROM stackoverflow.unicorns", function(unicorns) {
for(var i=0; i<unicorns.length; i++) {
alert("Unicorn! "+unicorns[i].name);
}
//add here your code, so that it's not executed until the query is ready
});
Note: as #radhakrishna pointed in a comment the open() function can also work in a synchronous manner if you pass true instead of false. This way the code will work as you were expecting: line after line, in other words: synchronously.
Callbacks can be used for a lot of things, for example:
function handleData(unicorns) {
//handle data... check if unicorns are purple
}
function queryError(error) {
alert("Error: "+error);
}
asyncOperation("SELECT * FROM stackoverflow.unicorns", handleData, queryError);
Here we are using two callbacks, one for handling the data and another one if an error occurs (of course that depends on how asyncOperation works, each async task has it's own callbacks).
Hi
I am trying to do an auto complete functionality for an input field.
psuedo code
<input type="text"/>
<script>
var ref,resp;//Global Variables
$('input').live('keyup',function(){
/* Get the input offset, so that list container can be palced at the bottom of the input once get the values through Ajax call */
ajaxCall();
/***
Here I want to write a code to create a div and place the values with in the div and show at the respective input field.
***/
});
function ajaxCall(){
var ref = new XMLHttpRequest();
ref.open();
ref.readStateChange = function(){
if(ref.readyState == 4 && ref.status ==200)
resp = ref.responseText();
}
ref.send();
}
</script>
The problem that I am getting here is, the part of the code that is after ajax call should be executed once ajax readyState is 4 and values are retrived.
But that code is being executed when readyState is 1(Its not being called after the other states) where the values are not retrieved from the database.The lets me unable to show the list.
Note: I know that the below part can be put in ajaxCall but it contains some variables which can be set at the place....
Does my problem make sense? If so,can some body let me know the solution...
You have to call the functions that depend on the result of the AJAX call during the AJAX callback. That's just how it is:
function ajaxCall(callback) {
var ref = new XMLHttpRequest();
ref.open();
ref.readStateChange = function() {
if (ref.readyState === 4 && ref.status === 200) {
resp = ref.responseText();
callback.call(null, ref.responseText); // invoke the callback here
}
}
ref.send();
}
and then:
ajaxCall(function(resp) {
// handle "resp" here
});
That said, please don't re-invent the wheel. You'll end up with code that's hard to maintain and not portable across browsers. There's plenty of libraries that make AJAX code like this a complete doddle. I use jQuery.
The code that you expect to run after the completion of ajax call should be put in the onSuccess() or onComplete() method of your code.
ajaxCall(function(resp) {
/***
Here I want to write a code to create a div and place the values with in the div and show at the respective input field.
***/
});
i.e.. This part of your code must come in the onComplete() method, whic will have the data returned by the ajax call as parameter.
I think this fairly basic but I can't seem to find one on-line. This can be in JavaScript or jquery.
I need to create a timer for about a 1-2 seconds.
Meanwhile another function is using ajax to pass data to a server side php file. When the response text gets back it displays it on the page.
At the moment I have the ajax function running and the time taken for the function to complete is about 0.1 seconds. But this makes the page look really jumpy as the content changes css styles while the ajax is waiting for a response and then back to the original on return (hope that makes sense).
Anyway to combat this I would like the function to check if the timer has ended before displaying the response text.
The only way I can get it at the moment is by creating a interval timer for a second and running the ajax function when that completes, but this is not ideal as the viewer MUST wait the extra second or 2 even if the request to the server takes over that time to complete.
Hope All Of That Makes Sense & Thanks Very Much For Your Time.
Chris
You're better off attaching your function as a "success" handler to your AJAX call rather than using a fixed timer. How you attach it depends on which library, if any:
jQuery 1.4 style (still works in 1.5)
$.ajax({
// your AJAX options
success: yourFunc
});
jQuery 1.5 style
$.ajax({
// your AJAX options
}).done(yourFunc);
DOM style
// after creating your XHR
xhr.onreadystatechange = function() {
if (this.readyState === 4) { // 4 means the request has completed
if (this.status !== 200) { // 200 is success, so anything else...
// log or report error
return;
}
// call your other function, which uses the AJAX data
yourFunc(this.responseText);
}
};
I would use a setTimeout or the jQuery .delay() if a timer is your only option.
$.ajax({
success: function() {
setTimout(function() {
// styling code goes here
}, 1000);
}
});
There is a JavaScript function, of which I have zero control of the code, which calls a function that I wrote. My function uses DOM to generate an iFrame, defines it's src and then appends it to another DOM element. However, before my function returns, and thus allows continued execution of the containing function, it is imperative that the iFrame be fully loaded.
Here are the things that I have tried and why they do not work :
1. The SetTimeout option :
99.999% of the time, this is THE answer. As a matter of fact, in the past decade that I have been mentoring in JavaScript, I have always insisted that code could always be refactored to use this option, and never believed a scenario existed where that was not the case. Well, I finally found one! The problem is that because my function is being called inline, if the very next line is executed before my iFrame finishes loading, it totally neuters my script, and since the moment my script completes, the external script continues. A callback of sorts will not work
2. The "Do nothing" loop :This option you use while(//iFrame is not loaded){//do nothing}. In theory this would not return until the frame is loaded. The problem is that since this hogs all the resources, the iFrame never loads. This trick, although horribly unprofessional, dirty etc. will work when you just need an inline delay, but since I require an external thread to complete, it will not.In FF, after a few seconds, it pauses the script and an alert pops up stating that there is an unresponsive script. While that alert is up, the iFrame is able to load, and then my function is able to return, but having the browser frozen for 10 seconds, and then requiring the user to correctly dismiss an error is a no go.
3. The model dialogue :
I was inspired by the fact that the FF popup allowed the iFrame to load while halting the execution of the function, and thinking about it, I realized that it is because the modal dialogue, is a way of halting execution yet allowing other threads to continue! Brilliant, so I decided to try other modal options. Things like alert() work beautifully! When it pops up, even if only up for 1/10th of a second, the iFrame is able to complete, and all works great. And just in case the 1/10 of a second is not sufficient, I can put the model dialogue in the while loop from solution 2, and it would ensure that the iFrame is loaded in time. Sweet right? Except for the fact that I now have to pop up a very unprofessional dialogue for the user to dismiss in order to run my script. I fought with myself about this cost/benefit of this action, but then I encountered a scenario where my code was called 10 times on a single page! Having to dismiss 10 alerts before acessing a page?! That reminds me of the late 90s script kiddie pages, and is NOT an option.
4. A gazillion other delay script out there:There are about 10 jQuery delay or sleep functions, some of them actually quite cleverly developed, but none worked. A few prototype options, and again, none I found could do it! A dozen or so other libraries and frameworks claimed they had what I needed, but alas they all conspired to give me false hope.
I am convinced that since a built in model dialogue can halt execution, while allowing other threads to continue, there must be some code accessible way to do the same thing with out user input.
The Code is literally thousands upon thousands of lines and is proprietary, so I wrote this little example of the problem for you to work with. It is important to note the ONLY code you are able to change is in the onlyThingYouCanChange function
Test File :
<html>
<head>
</head>
</html>
<body>
<div id='iFrameHolder'></div>
<script type='text/javascript'>
function unChangeableFunction()
{
new_iFrame = onlyThingYouCanChange(document.getElementById('iFrameHolder'));
new_iFrame_doc = (new_iFrame.contentWindow || new_iFrame.contentDocument);
if(new_iFrame_doc.document)new_iFrame_doc=new_iFrame_doc.document;
new_iFrame_body = new_iFrame_doc.body;
if(new_iFrame_body.innerHTML != 'Loaded?')
{
//The world explodes!!!
alert('you just blew up the world! Way to go!');
}
else
{
alert('wow, you did it! Way to go!');
}
}
var iFrameLoaded = false;
function onlyThingYouCanChange(objectToAppendIFrameTo)
{
iFrameLoaded = false;
iframe=document.createElement('iframe');
iframe.onload = new Function('iFrameLoaded = true');
iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure that must be maintained.
objectToAppendIFrameTo.appendChild(iframe);
var it = 0;
while(!iFrameLoaded) //I put the limit on here so you don't
{
//If I was able to put some sort of delay here that paused the exicution of the script, but did not halt all other browser threads, and did not require user interaction we'd be golden!
//alert('test'); //This would work if it did not require user interaction!
}
return iframe;
}
unChangeableFunction();
</script>
</body>
blank_frame.html :
<html>
<head>
</head>
<body style='margin:0px'>Loaded?</body>
</html>
HERE IS THE ANSWER I MADE FROM COMBINING IDEAS FROM RESPONDERS! YOU GUYS ROCK!
new source of the function I was allowed to change :
function onlyThingYouCanChange(objectToAppendIFrameTo)
{
iFrameLoaded = false;
iframe=document.createElement('iframe');
iframe.onload = new Function('iFrameLoaded = true');
iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure that must be maintained.
objectToAppendIFrameTo.appendChild(iframe);
var it = 0;
while(!iFrameLoaded) //I put the limit on here so you don't
{
if (window.XMLHttpRequest)
{
AJAX=new XMLHttpRequest();
}
else
{
AJAX=new ActiveXObject("Microsoft.XMLHTTP");
}
if (AJAX)
{
AJAX.open("GET", 'slow_page.php', false);
AJAX.send(null);
}
else
{
alert('something is wrong with AJAX!');
}
//If I was able to put some sort of delay here that paused the exicution of the script, but did not halt all other browser threads, and did not require user interaction we'd be golden!
//alert('test'); //This would work if it did not require user interaction!
}
return iframe;
}
slow_page.php :
<?
usleep(100000);//sleep for 1/10th of a second, to allow iFrame time to load without DOSing our own server!
?>
I do want to note that I stated that there was nothing outside of that function that I could change, and adding the php page did violate that "rule" but in may case I was able to do that. If I were not able to do that, I could have called blank_frame.html instead of slow_page.php, and it should have only ever needed to call it once (so 2 times per frame load) assuming that it responded in an identical amount of time as the iFrame load. If for some reason the iFrame load was slower, it might call it 2ce (a total of 3 calls to the server)
Yeah, the fact that javascript is single threaded really bites you here. You can use a synchronous ajax call to a purposefully slow page to emulate a sleep, but you aren't going to get the results you want. Why don't you just make sure that your IFrame is loaded before unchangeable function is called?
NB This is extremely hacky, and I wouldn't use it in any real-world situation. Among other potential issues, given sufficient traffic you could end up DDOSing yourself.
You could create sleep functionality by making non-asynchronous (A)JAX calls. In some older browsers this may freeze everything, but at least it won't require any kind of user response.
while (!iFrameLoaded)
{
if (XMLHTTPRequest) {
var request = new XMLHttpRequest();
} else {
var request = new ActiveXObject("Microsoft.XMLHTTP");
}
request.open('GET', 'anyoldfile.htm', false);
request.send();
// check if the iframe is loaded and set iFrameLoaded
}
What you really need is an event to be fired when the iFrame content has loaded. This is actually really easy because the page inside the iFrame has its own events and it can access scripts on the parent page. You will need to be able to change the contents of the iFrame though.
In your iFrame, you'll need this piece of code
// Use whichever DOMReady function you like, or window.onload would work
window.addEventListener('DOMContentLoaded', function() {
if (parent.window.myFunction) {
parent.window.myFunction();
}
}, false);
Then in your parent page, make a function called "myFunction" and put all the scripts you need to fire in there. This should work every time.
Edit: To get this to work you really need two functions. I'm assuming that's really not an option so we'll hack the one function to contain two functions and call the right part when we need it to.
function onlyThingYouCanChange(stringOrObject) {
function createIFrame(objectToAppendIFrameTo) {
// This comment represents all the code that appends your iFrame
}
function onIFrameReady() {
// This comment represents all the stuff you want to happen when the iFrame is ready
}
// The bones of it
if (stringOrObject === "iFrameLoaded") {
onIFrameReady();
} else {
createIFrame(stringOrObject);
}
}
The script in the iFrame should now be changed to something like this:
// Use whichever DOMReady function you like, or window.onload would work
window.addEventListener('DOMContentLoaded', function() {
if (parent.window.onlyThingYouCanChange) {
parent.window.onlyThingYouCanChange('iFrameLoaded');
}
}, false);
I haven't tested it, but in theory that should do it
A stupefyingly simple ;-} answer using XPCOM:
// Get instance of the XPCOM thread manager.
var threadManager=Components.classes['#mozilla.org/thread-manager;1'].getService(
Components.interfaces.nsIThreadManager);
// Release current thread.
function doThread() {threadManager.currentThread.processNextEvent(false);};
// Event enabled delay, time in ms.
function delay(time) {
var end;
var start=Date.now();
do {
end=Date.now();
doThread();
} while ((end-start) <= time);
}
Works in recent version of Firefox. Sorry no hope for Explorer!
A recursive function might help out in this case. just call the function until a global variable indicates that the frame is loaded
var iFrameStarted = false; //you need two global vars
var iFrameLoaded = false;
function onlyThingYouCanChange(objectToAppendIFrameTo)
{
if (iFrameLoaded=false) // if the frame has loaded then you are done. skip everything and return iframe
{ if (iFrameStarted = false) //otherwise start the frame if it has not been
{
iFrameStarted = true;
iframe=document.createElement('iframe');
iframe.onload = new Function('iFrameLoaded = true');
iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure
objectToAppendIFrameTo.appendChild(iframe);
var it = 0;
for (i=0;i<10000;i++) {} //slow down execution so you are not recursing yourself to death
onlyThingYouCanChange(objectToAppendIFrameTo); //start the recursion process
}
else //the frame has been started so continue recursion until the frame loaded
{
for (i=0;i<10000;i++) {} //slow down execution so you are not recursing yourself to death
onlyThingYouCanChange(objectToAppendIFrameTo); recursively call your function until the frame is loaded
}
}
return iframe; //you only get here when all the recursions are finished
}
Why can you not modify the base code? For example, it could be fairly simple to change the core function from
function unChangeableFunction()
{
new_iFrame = onlyThingYouCanChange(document.getElementById('iFrameHolder'));
new_iFrame_doc = (new_iFrame.contentWindow || new_iFrame.contentDocument);
if(new_iFrame_doc.document)new_iFrame_doc=new_iFrame_doc.document;
new_iFrame_body = new_iFrame_doc.body;
if(new_iFrame_body.innerHTML != 'Loaded?')
{
//The world explodes!!!
alert('you just blew up the world! Way to go!');
}
else
{
alert('wow, you did it! Way to go!');
}
}
To something like this:
function unChangeableFunction()
{
var new_iFrame = onlyThingYouCanChange(document.getElementById('iFrameHolder'));
new_iFrame.onload = function()
{
new_iFrame_doc = (new_iFrame.contentWindow || new_iFrame.contentDocument);
if(new_iFrame_doc.document)new_iFrame_doc=new_iFrame_doc.document;
new_iFrame_body = new_iFrame_doc.body;
if(new_iFrame_body.innerHTML != 'Loaded?')
{
//The world explodes!!!
alert('you just blew up the world! Way to go!');
}
else
{
alert('wow, you did it! Way to go!');
}
};
}
If that doesn't work for you, how about a transparent modification of the original code? Compile it with Javascript Strands and use the built-in futures support to handle this. Note that Javascript 1.7 also supports continuations, but would require changing the code manually to use them.
Another solution that may not be applicable, depending on how much you have simplified the original code. You could set an onload handler, then throw an error, then call unChangeableFunction in your onload handler:
function onlyThingYouCanChange(objectToAppendIFrameTo)
{
// using global variable func_called
if (!func_called) {
func_called = true;
var iframe=document.createElement('iframe');
iframe.src = 'blank_frame.html';
iframe.id = 'myIframe';
iframe.onload = function() {
unChangeableFunction();
};
objectToAppendIFrameTo.appendChild(iframe);
throw new Error('not an error');
} else {
return document.getElementById('myIframe');
}
}
This function (like unChangeableFunction) will be called twice: once in the first instance, then again when the onload handler is triggered. The two different pathways reflect this.
Again, this is hacky, and a definite abuse of JS's error functionality.
you can use cookie and setTimeout like that:
in blank_frame.html add a script:
<script type="text/javascript">
function deleteCookie(cookie_name)
{
var cookie_date=new Date();
cookie_date.setTime(cookie_date.getTime()-1);
document.cookie=cookie_name+="=;expires="+cookie_date.toGMTString();
}
function setCookie(name,value,expires,path,domain,secure){
document.cookie=name+"="+escape(value)+((expires)?"; expires="+expires.toGMTString():"")+((path)?"; path="+path:"")+((domain)?"; domain="+domain:"")+((secure)?"; secure":"");
}
window.onload=function(){
setCookie('iframe_loaded','yes',false,'/',false,false);
}
</script>
Basically you're adding a cookie iframe_loaded with value yes.
IMO it's better to remove the cookie as you need to do the same if you'll reload the page.
You can as well set the domain in setCookie function call.
Now in main file we'll use setTimeout with function that will check if the cookie exists, if it does then the function will return iframe like in your code:
function onlyThingYouCanChange(objectToAppendIFrameTo)
{
function get_cookie(cookie_name){
var results = document.cookie.match('(^|;) ?'+cookie_name+'=([^;]*)(;|$)');
return results?unescape(results[2]):null;
}
function deleteCookie(cookie_name){
var cookie_date=new Date();
cookie_date.setTime(cookie_date.getTime()-1);
document.cookie=cookie_name+="=;expires="+cookie_date.toGMTString();
}
iFrameLoaded = false;
iframe=document.createElement('iframe');
iframe.onload = new Function('iFrameLoaded = true');
iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure that must be maintained.
objectToAppendIFrameTo.appendChild(iframe);
var it = 0;
function checkiframe(){
if(get_cookie('iframe_loaded')=="yes"){
alert('iframe loaded');
deleteCookie('iframe_loaded');
return iframe;
}else{
setTimeout(checkiframe,1000);
}
}
checkiframe();
}
As a failsafe cookie is being deleted in this file as well.
Hopefully that will give you something to work with :)
Cheers
G.