views:

1981

answers:

3

I'm having some concurrency issues with a webpage I'm building. Basically, I have three script files that I'm using to hold some data:

script1.js:

`var myValue = 1;`

script2.js:

`var myValue = 2;`

script3.js:

`var myValue = 3;`

And I have one page, mypage.html that basically looks like this:

`<html>
 <script>
 function get_number()
 {
   var script_file = GetQueryStringValue( "run" );
   e = document.createElement('script');
   e.type='text/javascript';
   e.src = script_file + ".js"
   head.appendChild( e );

   document.write( myValue );
 }
 </script>
 <body onload="get_number()">

 <div onclick="get_number()">Click me!</div>
 </body>
 </html>`

The main idea with this page is that you would query it like this: mypage.html?run=script1 which would tell the get_number() function to dynamically insert script1.js in to mypage.htm. Then, I call get_number() to display the value loaded from the script.

Now, I've stripped down the above to what I think are the relevant parts and I've left out a bunch of stuff, obviously. My actual code loads a large array of values and is more interesting... But, I'm hoping someone can help me out with this regardless.

What I'm finding is that in IE, the number displays correctly.

In Firefox, Chrome and Safari, I get an error that myValue is undefined. However, if I click the Click me div that I created, the number displays correctly. So, I know I'm correctly loading the javascript external file. But, it just isn't loaded in time for my get_number() function to work correctly onload.

Now, I hacked up my file a little so that the get_number function looks like this:

 function get_number()
 {
   var script_file = GetQueryStringValue( "run" );
   e = document.createElement('script');
   e.type='text/javascript';
   e.src = script_file + ".js"
   head.appendChild( e );

   setTimeout( function() { document.write( myValue ), 10 } );
 }

The setTimeout delays enough for the DOM to be updated and the javascript to be loaded in most cases. But, this is a total hack. Firefox tends to load this 'improved' code correctly all the time while Chrome and Safari manage to get the value about 50% of the time.

So, I guess I'm wondering, is there a better way to accomplish what I'm trying to do here? It's very important that the value be driven externally (by the query string), but other than that requirement, I'm very flexible.

+3  A: 

This method of dynamically loading script files using DOM methods like head.appendChild is asynchronous, meaning that the page does not wait for it to load before running any further code. If you did not want this asynchronous behaviour, you could load them with regular elements in your HTML, or you could mess around with a 'synchronous' XMLHttpRequest to grab it and eval() to run it.

You probably don't want to do that, though, because being asynchronous makes the entire page load faster, anyway. You will probably just need to add some logic that waits for the dynamically loaded script to have loaded before you go on with the next bit of code. This may, however, involve polling using setInterval() until you notice a variable from the included script has been defined. Or, you could add code in the included script that calls a method to whatever has been waiting for it.

Or, you could look at how jQuery does something similar, look for where the ajax method is defined, specifically these parts...

var script = document.createElement("script");

// then later ...

script.onload = script.onreadystatechange = function(){
    if ( !done && (!this.readyState ||
    this.readyState == "loaded" || this.readyState == "complete") ) {

        // do processing
            head.removeChild( script );
    }
};

// ...

head.appendChild(script);
thomasrutter
A: 

Have everything in the same script. then use a function pointer:

Your script file:

function myFunction1()
{
  return 1;
}

function myFunction2()
{
  return 2;
}

function myFunction3()
{
  return 3;
}

And to choose function (main file)

var myFunction;
switch (GetQueryStringValue("function"))
{
  case "1":
    myFunction = myFunction1;
    break;
  case "2":
    myFunction = myFunction2;
    break;
  case "3":
    myFunction = myFunction3;
    break;
}

var value = myFunction();

I'm sure that you can use eval() or something instead of the switch statement.

jgauffin
+2  A: 

Could you possibly add a function call to your script files, so that when they're run, they call the function that does your

document.write( myValue );

So that script1.js would be

var myValue = 1;
scriptLoaded();

and your main file would have:

function scriptLoaded(){
   document.write( myValue );
}
Mikko Tapionlinna
This is the solution I went with because it proved to be so simple. I feel somewhat silly that I didn't think of this myself!Thanks for the other replies, though. They were all actually workable and would have solved me problem.
Daniel Jette