tags:

views:

414

answers:

5

I have a script that knows to load dynamiclly scripts that contains javascript classes. i'm loading the class script using the following code:

var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "myscript.js";
head.appendChild(script);

i'm then trying to create the new class using eval:

var classObj = eval(" new MyClass()" );

the problem is that the code of the eval is being executed bofre the script has been loaded into memory and i get an error that the MyClass is undefined.

Is there a way to synch these events? i need to make sure the script is fully being loaded into memory before i can start allocating classes from it.

A: 

Are you sure that adding a <script> element to the DOM will cause the browser to actually evaluate the script at all? I have a vague memory of reading somewhere that it doesn't, but perhaps I inhaled a bit too much oven cleaner yesterday.

Rob
yes. its called On-Demand JavaScript. you can read more about it here: http://www.webreference.com/programming/javascript/rg29/
Amir
+1  A: 

Amir, it looks to me as if the script is still sitting on the server, that is to say, has not been loaded by the browser. So your head.appendChild(script);is appending null. You can't fetch a script from the server just by saying its name, you need to request it and inject it into the page, using ajax, or by loading it in using <script> tag.

karim79
i thought it at first. i debbuged it and saw that the script is becoming available after a few milliseconds. i added a alert("i'm loaded"); into the script and saw it executed.
Amir
A: 

Use the jQuery (a JavaScript library) getScript function to load your script async. Use the callback function to create your objects. Example:

$.getScript("script.js", function () {
  var classObj = new MyClass();
});

I would like to mention that Microsoft supports JQuery, and they will make support for it in upcoming versions of Visual Studio. For more information please visit http://live.visitmix.com/.

knut
+2  A: 

You need to attach an event handler to either the onload method, in browsers compliant with Web standards, or the onreadystatechange, checking for the script.readyState property getting equal to "loaded" or "complete", in Internet Explorer.

Before you get notified that the script was loaded, you are probably trying to access objects, functions and properties that have not been declared or created yet.

Here is an example function, extracted from the module bezen.dom.js in my Javascript library, bezen.org:

var appendScript = function(parent, scriptElt, listener) {
    // append a script element as last child in parent and configure 
    // provided listener function for the script load event
    //
    // params:
    //   parent - (DOM element) (!nil) the parent node to append the script to
    //   scriptElt - (DOM element) (!nil) a new script element 
    //   listener - (function) (!nil) listener function for script load event
    //
    // Notes:
    //   - in IE, the load event is simulated by setting an intermediate 
    //     listener to onreadystate which filters events and fires the
    //     callback just once when the state is "loaded" or "complete"
    //
    //   - Opera supports both readyState and onload, but does not behave in
    //     the exact same way as IE for readyState, e.g. "loaded" may be
    //     reached before the script runs.

    var safelistener = catchError(listener,'script.onload');

    // Opera has readyState too, but does not behave in a consistent way
    if (scriptElt.readyState && scriptElt.onload!==null) {
      // IE only (onload===undefined) not Opera (onload===null)
      scriptElt.onreadystatechange = function() {
        if ( scriptElt.readyState === "loaded" || 
             scriptElt.readyState === "complete" ) {
          // Avoid memory leaks (and duplicate call to callback) in IE
          scriptElt.onreadystatechange = null;
          safelistener();
        }
      };
    } else {
      // other browsers (DOM Level 0)
      scriptElt.onload = safelistener;
    }
    parent.appendChild( scriptElt );
};

To adapt it to your needs, you may replace the call to catchError, which wraps the listener to catch and log errors, and use the modified function:

var appendScript = function(parent, scriptElt, listener) {
    // append a script element as last child in parent and configure 
    // provided listener function for the script load event
    //
    // params:
    //   parent - (DOM element) (!nil) the parent node to append the script to
    //   scriptElt - (DOM element) (!nil) a new script element 
    //   listener - (function) (!nil) listener function for script load event
    //
    // Notes:
    //   - in IE, the load event is simulated by setting an intermediate 
    //     listener to onreadystate which filters events and fires the
    //     callback just once when the state is "loaded" or "complete"
    //
    //   - Opera supports both readyState and onload, but does not behave in
    //     the exact same way as IE for readyState, e.g. "loaded" may be
    //     reached before the script runs.

    var safelistener = function(){
      try {
        listener();
      } catch(e) {
        // do something with the error
      }
    };

    // Opera has readyState too, but does not behave in a consistent way
    if (scriptElt.readyState && scriptElt.onload!==null) {
      // IE only (onload===undefined) not Opera (onload===null)
      scriptElt.onreadystatechange = function() {
        if ( scriptElt.readyState === "loaded" || 
             scriptElt.readyState === "complete" ) {
          // Avoid memory leaks (and duplicate call to callback) in IE
          scriptElt.onreadystatechange = null;
          safelistener();
        }
      };
    } else {
      // other browsers (DOM Level 0)
      scriptElt.onload = safelistener;
    }
    parent.appendChild( scriptElt );
};
Eric Bréchemier
+1  A: 

Since you seem to be able to edit the external script (since you tested it with an alert), why not just put this code in that script?

If you can't do that (maybe the extra code is generated or the first file is shared perhaps), just add a function call at the end of the script you're loading like this:

load_complete();

and then put your extra code in that function:

function load_complete() {
    var classObj = eval(" new MyClass()" );
}

It's a lot simpler and foolproof than any kind of onload trigger. Also, if the js file is shared, then you can have different load_complete functions on every page that uses it (just be sure to always define a load_complete, even if it is empty).

Rob Van Dam