2009-04-21 13 views
10

Tengo un script que sabe cargar scripts dynamiclly que contienen clases de JavaScript. Estoy cargando el guión de clase utilizando el siguiente código:secuencia de comandos dinámica carga de sincronización

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

i entonces estoy tratando de crear la nueva clase utilizando eval:

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

El problema es que el código de la función eval es siendo ejecutado bofre la secuencia de comandos se ha cargado en la memoria y me sale un error que el MyClass is undefined.

¿Hay alguna manera de sincronizar estos eventos? Necesito asegurarme de que la secuencia de comandos se cargue completamente en la memoria antes de que pueda comenzar a asignar clases desde ella.

+0

Comprobar esta respuesta relacionada a cabo: ** [Cargar ordenación de etiquetas de script añadidas dinámicamente] (http://stackoverflow.com/a/38840724/2247494) ** – jherax

Respuesta

0

¿Estás seguro de que agregar un elemento <script> al DOM hará que el navegador realmente evalúe el script? Tengo un vago recuerdo de leer en algún lado que no, pero quizás inhalé demasiado limpiador de horno ayer.

+1

sí. se llama JavaScript bajo demanda. Puede leer más sobre esto aquí: http://www.webreference.com/programming/javascript/rg29/ – Amir

0

Amir, me parece que el script todavía está en el servidor, es decir, no ha sido cargado por el navegador. Por lo tanto, su head.appendChild(script); se está agregando nulo. No puede obtener un script del servidor simplemente diciendo su nombre, debe solicitarlo e insertarlo en la página, utilizando ajax, o cargándolo con la etiqueta <script>.

+0

lo pensé al principio. Lo eliminé y vi que el script está disponible después de unos pocos milisegundos. agregué una alerta ("estoy cargado"); en el script y lo vio ejecutado. – Amir

+0

-1 Agrega el script _element_ inmediatamente, pero carga y ejecuta el script _code_ de forma asincrónica, por lo que la clase no está disponible inmediatamente. –

1

Utilice la jQuery (una biblioteca JavaScript) getScript función para cargar su secuencia de comandos asíncrona. Use la función de devolución de llamada para crear sus objetos. Ejemplo:

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

Esto no es confiable, de acuerdo con la documentación 'getScript()': "La devolución de llamada se activa una vez que el script se ha cargado pero no necesariamente se ha ejecutado". –

5

Es necesario adjuntar un controlador de eventos para el método de proceso de carga, en los navegadores compatibles con los estándares de la Web, o el onreadystatechange, la comprobación de la propiedad script.readyState conseguir igual a "cargado" o "completa" , en Internet Explorer.

Antes de que te notifiquen que la secuencia de comandos se cargó, probablemente estés intentando acceder a objetos, funciones y propiedades que aún no se han declarado o creado.

Aquí es una función de ejemplo, extraído del módulo de bezen.dom.js en mi 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); 
}; 

para adaptarlo a sus necesidades, puede reemplazar la llamada a catchError, que envuelve al oyente a atrapar y registro de errores y utilizar la función de modificación:

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

ya que parece ser capaz de editar el script externo (ya que probamos con una alerta), ¿por qué no poner este código en ese guión?

Si no puede hacer eso (tal vez se genera el código extra o el primer archivo se comparte tal vez), basta con añadir una llamada de función al final de la secuencia de comandos que está cargando la siguiente manera:

load_complete(); 

y luego poner su código extra en esa función:

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

Es mucho más simple e infalible que cualquier tipo de gatillo proceso de carga. Además, si el archivo js se comparte, puede tener diferentes funciones load_complete en cada página que lo use (solo asegúrese de definir siempre un load_complete, incluso si está vacío).

1

creo que en realidad esto puede ser remediado por asegurarse de que usted pone el código de cargar el script externo y el código usando las secuencias de comandos externa bloques de script separados como esta:

<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> 
Cuestiones relacionadas