Cargar dinámicamente un archivo JavaScript

Resuelto Adam asked hace 16 años • 29 respuestas

¿Cómo se puede cargar de forma fiable y dinámica un archivo JavaScript? Esto se puede utilizar para implementar un módulo o componente que, cuando se "inicializa", el componente cargará dinámicamente todos los scripts de la biblioteca JavaScript necesarios a pedido.

El cliente que utiliza el componente no necesita cargar todos los archivos de secuencia de comandos de la biblioteca (e insertar <script>etiquetas manualmente en su página web) que implementan este componente, solo el archivo de secuencia de comandos del componente "principal".

¿Cómo logran esto las bibliotecas de JavaScript convencionales (Prototype, jQuery, etc.)? ¿Estas herramientas combinan varios archivos JavaScript en una única versión redistribuible de "compilación" de un archivo de script? ¿O realizan alguna carga dinámica de scripts auxiliares de 'biblioteca'?

Una adición a esta pregunta: ¿ hay alguna manera de manejar el evento después de cargar un archivo JavaScript incluido dinámicamente? El prototipo tiene document.observepara eventos de todo el documento. Ejemplo:

document.observe("dom:loaded", function() {
  // initially hide all containers for tab content
  $$('div.tabcontent').invoke('hide');
});

¿Cuáles son los eventos disponibles para un elemento de script?

Adam avatar Aug 22 '08 04:08 Adam
Aceptado

No hay importación/inclusión/requisito en javascript, pero hay dos formas principales de lograr lo que desea:

1 - Puedes cargarlo con una llamada AJAX y luego usar eval.

Esta es la forma más sencilla, pero está limitada a su dominio debido a la configuración de seguridad de Javascript, y el uso de eval abre la puerta a errores y hacks.

2 - Agregue un elemento de secuencia de comandos con la URL del script en el HTML.

Definitivamente el mejor camino a seguir. Puede cargar el script incluso desde un servidor externo y está limpio ya que utiliza el analizador del navegador para evaluar el código. Puede colocar el scriptelemento en el headelemento de la página web o en la parte inferior del archivo body.

Ambas soluciones se analizan e ilustran aquí.

Ahora bien, hay un gran problema que debes conocer. Hacer eso implica que cargas el código de forma remota. Los navegadores web modernos cargarán el archivo y seguirán ejecutando el script actual porque cargan todo de forma asincrónica para mejorar el rendimiento.

Significa que si usa estos trucos directamente, no podrá usar el código recién cargado en la siguiente línea después de haber solicitado que se cargue, porque aún se estará cargando.

Por ejemplo: my_lovely_script.js contiene MySuperObject

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

js.type = "text/javascript";
js.src = jsFilePath;

document.body.appendChild(js);

var s = new MySuperObject();

Error : MySuperObject is undefined

Luego recargas la página presionando F5. ¡Y funciona! Confuso...

Entonces, ¿qué hacer al respecto?

Bueno, puedes usar el truco que sugiere el autor en el enlace que te di. En resumen, para las personas que tienen prisa, utiliza en event para ejecutar una función de devolución de llamada cuando se carga el script. Entonces puedes poner todo el código usando la biblioteca remota en la función de devolución de llamada. P.EJ :

function loadScript(url, callback)
{
    // adding the script element to the head as suggested before
   var head = document.getElementsByTagName('head')[0];
   var script = document.createElement('script');
   script.type = 'text/javascript';
   script.src = url;

   // then bind the event to the callback function 
   // there are several events for cross browser compatibility
   script.onreadystatechange = callback;
   script.onload = callback;

   // fire the loading
   head.appendChild(script);
}

Luego escribe el código que desea usar DESPUÉS de cargar el script en una función lambda:

var myPrettyCode = function() {
    // here, do what ever you want
};

Luego ejecutas todo eso:

loadScript("my_lovely_script.js", myPrettyCode);

Bien, lo tengo. Pero es doloroso escribir todo esto.

Bueno, en ese caso, puedes usar como siempre el fantástico framework gratuito jQuery, que te permite hacer exactamente lo mismo en una sola línea:

$.getScript("my_lovely_script.js", function() {
    alert("Script loaded and executed.");
    // here you can use anything you defined in the loaded script
});
 avatar Feb 09 '2013 09:02

Puedes crear un elemento de script dinámicamente, usando Prototipos :

new Element("script", {src: "myBigCodeLibrary.js", type: "text/javascript"});

El problema aquí es que no sabemos cuándo el archivo de script externo está completamente cargado.

A menudo queremos que nuestro código dependiente esté en la siguiente línea y nos gusta escribir algo como:

if (iNeedSomeMore) {
    Script.load("myBigCodeLibrary.js"); // includes code for myFancyMethod();
    myFancyMethod(); // cool, no need for callbacks!
}

Existe una forma inteligente de inyectar dependencias de scripts sin necesidad de devoluciones de llamada. Simplemente tiene que extraer el script mediante una solicitud AJAX sincrónica y evaluarlo a nivel global.

Si usa Prototype, el método Script.load se ve así:

var Script = {
    _loadedScripts: [],
    include: function(script) {
        // include script only once
        if (this._loadedScripts.include(script)) {
            return false;
        }
        // request file synchronous
        var code = new Ajax.Request(script, {
            asynchronous: false,
            method: "GET",
            evalJS: false,
            evalJSON: false
        }).transport.responseText;
        // eval code on global level
        if (Prototype.Browser.IE) {
            window.execScript(code);
        } else if (Prototype.Browser.WebKit) {
            $$("head").first().insert(Object.extend(
                new Element("script", {
                    type: "text/javascript"
                }), {
                    text: code
                }
            ));
        } else {
            window.eval(code);
        }
        // remember included script
        this._loadedScripts.push(script);
    }
};
aemkei avatar Oct 28 '2008 09:10 aemkei