Cargar dinámicamente un archivo JavaScript
¿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.observe
para 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?
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 script
elemento en el head
elemento 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
});
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);
}
};