最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - dynamic script loading synchronization - Stack Overflow

programmeradmin0浏览0评论

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.

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.

Share Improve this question asked Apr 21, 2009 at 21:39 AmirAmir 1,2593 gold badges18 silver badges32 bronze badges 1
  • Check this related answer out: Load ordering of dynamically added script tags – jherax Commented Aug 9, 2016 at 17:05
Add a comment  | 

6 Answers 6

Reset to default 5

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 );
};

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).

I believe that this can actually be remedied by making sure you put the code loading the external script and the code using the external script separate script blocks like this:

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

//this is in the same script block so it wouldn't work
//var classObj = eval(" new MyClass()" );
</script>

<script>
//this is in a separate script block so it will work
var classObj = eval(" new MyClass()" );
</script>

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();
});

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.

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.

发布评论

评论列表(0)

  1. 暂无评论