Cargue dinámicamente un archivo JavaScript

168

¿Cómo puede cargar de manera confiable y dinámica un archivo JavaScript? Esto se puede utilizar para implementar un módulo o componente que, cuando se 'inicialice', el componente cargará dinámicamente todos los scripts de biblioteca JavaScript necesarios bajo demanda.

No es necesario que el cliente que usa el componente cargue todos los archivos de script de la biblioteca (e inserte <script>etiquetas manualmente en su página web) que implementen este componente, solo el archivo de script del componente 'principal'.

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

Una adición a esta pregunta: ¿hay alguna forma de manejar el evento después de cargar un archivo JavaScript incluido dinámicamente? 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?

Adán
fuente

Respuestas:

84

Puede escribir etiquetas de script dinámico (usando Prototype ):

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 nuestro código dependiente 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!
}

Hay una forma inteligente de inyectar dependencias de script sin la necesidad de devoluciones de llamada. Simplemente tiene que extraer el script a través de una solicitud AJAX sincrónica y evaluar el script 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
fuente
¿Qué puedo hacer para que funcione para dominios cruzados? (cargando script desde http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
@ user2284570 CORS stackoverflow.com/questions/5750696/…
Ciasto piekarz
@Ciastopiekarz: no controlo web.archive.org.
user2284570
Luego debe raspar los datos a los que desea acceder utilizando algún otro programa y proporcionárselos a usted mismo
David Schumann
¿Qué es Ajax.Request ??
bluejayke
72

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

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

Esta es la forma más directa, 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 piratas informáticos.

2 - Agregue una etiqueta de script 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 a medida que usa el analizador del navegador para evaluar el código. Puede poner la etiqueta en el encabezado de la página web o en la parte inferior del cuerpo.

Ambas soluciones se discuten e ilustran aquí.

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

Significa que si usa estos trucos directamente, no podrá usar su código recién cargado la siguiente línea después de que solicitó que se cargue, porque todavía se cargará.

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 vuelve a cargar 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, usa un evento para ejecutar una función de devolución de llamada cuando se carga el script. Para que pueda 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 tag 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 que el script se carga en una función lambda:

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

Entonces ejecutas todo eso:

loadScript("my_lovely_script.js", myPrettyCode);

Ok, lo tengo Pero es un dolor escribir todo esto.

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

$.getScript("my_lovely_script.js", function() {
    alert("Script loaded and executed.");
    // here you can use anything you defined in the loaded script
});
Dhaval Shah
fuente
11
No puedo creer lo subestimada que es esta respuesta. Gracias.
naftalimich
2
Apuesto a que es porque a la gente no le gusta leer más allá de la primera línea, a menos que les prometa que serán ricos si "simplemente siguen estos tres pasos secretos para el éxito".
Costa
Esto funciona. Espero que compartir mi experiencia aquí le ahorre tiempo porque pasé muchas horas en esto. Estoy usando Angular 6 y plantilla aplicada (html, css, jquery) El problema es que la plantilla tiene un archivo js que se carga después de que los elementos html se cargan para adjuntar eventos de escucha. Ese archivo js fue difícil de ejecutar después de cargar la aplicación angular. Agregarlo a la etiqueta de scripts de la aplicación angular (angular.json) los agrupará pero no ejecutará ese archivo js después de cargarlo. Es demasiado código para reescribir en mecanografiado, por lo que fue de gran ayuda. Siguiente comentario Voy a poner el ejemplo debido a la longitud del comentario
Nour Lababidi
Simplemente utilicé este código de la siguiente manera: ngAfterViewInit () {depurador; $ .getScript ("/ assets / js / jquery.app.js", function () {alert ("Script cargado y ejecutado"); // aquí puede usar cualquier cosa que haya definido en el script cargado}); }
Nour Lababidi
Para el '$' en angular, seguí esto: stackoverflow.com/questions/32050645/…
Nour Lababidi el
28

Utilicé una versión mucho menos complicada recientemente con jQuery :

<script src="scripts/jquery.js"></script>
<script>
  var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
  var $head = $("head");
  for (var i = 0; i < js.length; i++) {
    $head.append("<script src=\"" + js[i] + "\"></scr" + "ipt>");
  }
</script>

Funcionó muy bien en todos los navegadores en los que lo probé: IE6 / 7, Firefox, Safari, Opera.

Actualización: versión sin jQuery:

<script>
  var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
  for (var i = 0, l = js.length; i < l; i++) {
    document.getElementsByTagName("head")[0].innerHTML += ("<script src=\"" + js[i] + "\"></scr" + "ipt>");
  }
</script>
travis
fuente
25
Eso es genial ... a menos que intentes cargar jquery.
1
Parece que jQuery incluirá el complemento Requerir en jQuery Core para un lanzamiento futuro: plugins.jquery.com/project/require
Adam
1
Una forma aún mejor de usar jQuery es usar $.getScript. Mira mi respuesta.
Muhd
1
Modernizr (yepnope.js) o lab.js son las soluciones apropiadas para esto. El uso de una biblioteca de script pesada (que debe cargarse primero) no es la respuesta más adecuada para dispositivos móviles o para muchas otras situaciones.
1nfiniti
2
Los navegadores más antiguos @MaulikGangani y los validadores html lo interpretarían como el token para finalizar el script.
travis
20

Básicamente hice lo mismo que hiciste Adam, pero con una ligera modificación para asegurarme de que estaba agregando a la etiqueta de la cabeza para hacer el trabajo. Simplemente creé una función de inclusión (código a continuación) para manejar archivos de script y css.

Esta función también verifica para asegurarse de que el script o el archivo CSS no se haya cargado dinámicamente. No verifica los valores codificados a mano y puede haber habido una mejor manera de hacerlo, pero sirvió para el propósito.

function include( url, type ){
    // First make sure it hasn't been loaded by something else.
    if( Array.contains( includedFile, url ) )
        return;

    // Determine the MIME-type
    var jsExpr = new RegExp( "js$", "i" );
    var cssExpr = new RegExp( "css$", "i" );
    if( type == null )
        if( jsExpr.test( url ) )
            type = 'text/javascript';
        else if( cssExpr.test( url ) )
            type = 'text/css';

    // Create the appropriate element.
    var tag = null;
    switch( type ){
        case 'text/javascript' :
            tag = document.createElement( 'script' );
            tag.type = type;
            tag.src = url;
            break;
        case 'text/css' :
            tag = document.createElement( 'link' );
            tag.rel = 'stylesheet';
            tag.type = type;
            tag.href = url;
            break;
    }

    // Insert it to the <head> and the array to ensure it is not
    // loaded again.
    document.getElementsByTagName("head")[0].appendChild( tag );
    Array.add( includedFile, url );
}
caballo pálido
fuente
Sin más contexto que ese Pickle, me temo que no tengo ninguna sugerencia. El código anterior funciona tal cual, se extrajo directamente de un proyecto en funcionamiento.
palehorse
55
@palehorse, Mike y Muhd, tienen razón, podría funcionar en su proyecto, pero eso se debe a que las variables "includedFile" y "Array" deben definirse en otra parte de su proyecto, este código por sí solo no se ejecutará, podría ser mejor editarlo para que pueda funcionar fuera del contexto de su proyecto, o al menos agregar un comentario que explique cuáles son esas variables indefinidas (tipo, etc.)
user280109
14

otra respuesta asombrosa

$.getScript("my_lovely_script.js", function(){


   alert("Script loaded and executed.");
  // here you can use anything you defined in the loaded script

 });

https://stackoverflow.com/a/950146/671046

Naveed
fuente
2
¿Qué puedo hacer para que funcione para dominios cruzados? (cargando script desde http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
9

Aquí hay un código de ejemplo que he encontrado ... ¿Alguien tiene una mejor manera?

  function include(url)
  {
    var s = document.createElement("script");
    s.setAttribute("type", "text/javascript");
    s.setAttribute("src", url);
    var nodes = document.getElementsByTagName("*");
    var node = nodes[nodes.length -1].parentNode;
    node.appendChild(s);
  }
Adán
fuente
¿Qué puedo hacer para que funcione para dominios cruzados? (cargando script desde http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
6

Si ya tiene jQuery cargado, debe usar $ .getScript .

Esto tiene una ventaja sobre las otras respuestas aquí en que tiene una función de devolución de llamada integrada (para garantizar que el script se cargue antes de que se ejecute el código dependiente) y puede controlar el almacenamiento en caché.

Muhd
fuente
4

Si desea cargar un script SYNC , debe agregar el texto del script directamente a la etiqueta HEAD HTML. Agregarlo como activará una carga ASYNC . Para cargar el texto del script desde un archivo externo sincrónicamente, use XHR. Debajo de una muestra rápida (está usando partes de otras respuestas en esta y otras publicaciones):

/*sample requires an additional method for array prototype:*/

if (Array.prototype.contains === undefined) {
Array.prototype.contains = function (obj) {
    var i = this.length;
    while (i--) { if (this[i] === obj) return true; }
    return false;
};
};

/*define object that will wrap our logic*/
var ScriptLoader = {
LoadedFiles: [],

LoadFile: function (url) {
    var self = this;
    if (this.LoadedFiles.contains(url)) return;

    var xhr = new XMLHttpRequest();
    xhr.onload = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                self.LoadedFiles.push(url);
                self.AddScript(xhr.responseText);
            } else {
                if (console) console.error(xhr.statusText);
            }
        }
    };
    xhr.open("GET", url, false);/*last parameter defines if call is async or not*/
    xhr.send(null);
},

AddScript: function (code) {
    var oNew = document.createElement("script");
    oNew.type = "text/javascript";
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);
}
};

/*Load script file. ScriptLoader will check if you try to load a file that has already been loaded (this check might be better, but I'm lazy).*/

ScriptLoader.LoadFile("Scripts/jquery-2.0.1.min.js");
ScriptLoader.LoadFile("Scripts/jquery-2.0.1.min.js");
/*this will be executed right after upper lines. It requires jquery to execute. It requires a HTML input with id "tb1"*/
$(function () { alert($('#tb1').val()); });
Sielu
fuente
3

¿Alguien tiene una mejor manera?

Creo que solo agregar el script al cuerpo sería más fácil que agregarlo al último nodo de la página. Qué tal esto:

function include(url) {
  var s = document.createElement("script");
  s.setAttribute("type", "text/javascript");
  s.setAttribute("src", url);
  document.body.appendChild(s);
}
Joseph Pecoraro
fuente
¿Qué puedo hacer para que funcione para dominios cruzados? (cargando script desde http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
3

He usado otra solución que encontré en la red ... esta está bajo creativecommons y comprueba si la fuente se incluyó antes de llamar a la función ...

Puede encontrar el archivo aquí: include.js

/** include - including .js files from JS - [email protected] - 2005-02-09
 ** Code licensed under Creative Commons Attribution-ShareAlike License 
 ** http://creativecommons.org/licenses/by-sa/2.0/
 **/              
var hIncludes = null;
function include(sURI)
{   
  if (document.getElementsByTagName)
  {   
    if (!hIncludes)
    {
      hIncludes = {}; 
      var cScripts = document.getElementsByTagName("script");
      for (var i=0,len=cScripts.length; i < len; i++)
        if (cScripts[i].src) hIncludes[cScripts[i].src] = true;
    }
    if (!hIncludes[sURI])
    {
      var oNew = document.createElement("script");
      oNew.type = "text/javascript";
      oNew.src = sURI;
      hIncludes[sURI]=true;
      document.getElementsByTagName("head")[0].appendChild(oNew);
    }
  }   
} 
Pierre Spring
fuente
¿Qué puedo hacer para que funcione para dominios cruzados? (cargando script desde http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
3

Acabo de enterarme de una gran característica en YUI 3 (en el momento de la escritura disponible en versión preliminar). Puede insertar fácilmente dependencias en bibliotecas YUI y en módulos "externos" (lo que está buscando) sin demasiado código: YUI Loader .

También responde a su segunda pregunta sobre la función que se llama tan pronto como se carga el módulo externo.

Ejemplo:

YUI({
    modules: {
        'simple': {
            fullpath: "http://example.com/public/js/simple.js"
        },
        'complicated': {
            fullpath: "http://example.com/public/js/complicated.js"
            requires: ['simple']  // <-- dependency to 'simple' module
        }
    },
    timeout: 10000
}).use('complicated', function(Y, result) {
    // called as soon as 'complicated' is loaded
    if (!result.success) {
        // loading failed, or timeout
        handleError(result.msg);
    } else {
        // call a function that needs 'complicated'
        doSomethingComplicated(...);
    }
});

Funcionó perfectamente para mí y tiene la ventaja de administrar dependencias. Consulte la documentación de YUI para ver un ejemplo con el calendario YUI 2 .

Kariem
fuente
Esto probablemente sea ideal, excepto que YUI es obscenamente grande solo para esta funcionalidad.
Muhd
3

Hay un nuevo estándar ECMA propuesto llamado importación dinámica , recientemente incorporado en Chrome y Safari.

const moduleSpecifier = './dir/someModule.js';

import(moduleSpecifier)
   .then(someModule => someModule.foo()); // executes foo method in someModule
Alister
fuente
2

La técnica que utilizamos en el trabajo es solicitar el archivo javascript usando una solicitud AJAX y luego evaluar () la devolución. Si está utilizando la biblioteca de prototipos, admiten esta funcionalidad en su llamada Ajax.Request.

17 de 26
fuente
2

jquery resolvió esto por mí con su función .append () : lo usé para cargar el paquete completo de jquery ui

/*
 * FILENAME : project.library.js
 * USAGE    : loads any javascript library
 */
    var dirPath = "../js/";
    var library = ["functions.js","swfobject.js","jquery.jeditable.mini.js","jquery-ui-1.8.8.custom.min.js","ui/jquery.ui.core.min.js","ui/jquery.ui.widget.min.js","ui/jquery.ui.position.min.js","ui/jquery.ui.button.min.js","ui/jquery.ui.mouse.min.js","ui/jquery.ui.dialog.min.js","ui/jquery.effects.core.min.js","ui/jquery.effects.blind.min.js","ui/jquery.effects.fade.min.js","ui/jquery.effects.slide.min.js","ui/jquery.effects.transfer.min.js"];

    for(var script in library){
        $('head').append('<script type="text/javascript" src="' + dirPath + library[script] + '"></script>');
    }

Para usar : en el encabezado de su html / php / etc después de importar jquery.js, simplemente incluiría este archivo para cargarlo en la totalidad de su biblioteca y agregarlo al encabezado ...

<script type="text/javascript" src="project.library.js"></script>
JM Design
fuente
2

¡Mantenlo agradable, corto, simple y fácil de mantener! :]

// 3rd party plugins / script (don't forget the full path is necessary)
var FULL_PATH = '', s =
[
    FULL_PATH + 'plugins/script.js'      // Script example
    FULL_PATH + 'plugins/jquery.1.2.js', // jQuery Library 
    FULL_PATH + 'plugins/crypto-js/hmac-sha1.js',      // CryptoJS
    FULL_PATH + 'plugins/crypto-js/enc-base64-min.js'  // CryptoJS
];

function load(url)
{
    var ajax = new XMLHttpRequest();
    ajax.open('GET', url, false);
    ajax.onreadystatechange = function ()
    {
        var script = ajax.response || ajax.responseText;
        if (ajax.readyState === 4)
        {
            switch(ajax.status)
            {
                case 200:
                    eval.apply( window, [script] );
                    console.log("library loaded: ", url);
                    break;
                default:
                    console.log("ERROR: library not loaded: ", url);
            }
        }
    };
    ajax.send(null);
}

 // initialize a single load 
load('plugins/script.js');

// initialize a full load of scripts
if (s.length > 0)
{
    for (i = 0; i < s.length; i++)
    {
        load(s[i]);
    }
}

Este código es simplemente un breve ejemplo funcional que podría requerir funcionalidades de funciones adicionales para un soporte completo en cualquier plataforma (o dada).

tfont
fuente
2

La importación del módulo dinámico aterrizó en Firefox 67+ .

(async () => {
   await import('./synth/BubbleSynth.js')
})()

Con manejo de errores:

(async () => {
    await import('./synth/BubbleSynth.js').catch((error) => console.log('Loading failed' + error))
})()

También funciona para cualquier tipo de bibliotecas que no sean módulos, en este caso, la lib está disponible en el objeto de ventana, a la antigua usanza, pero solo a pedido, lo cual es bueno.

Ejemplo usando suncalc.js , ¡el servidor debe tener CORS habilitado para funcionar de esta manera!

(async () => {
 await import('https://cdnjs.cloudflare.com/ajax/libs/suncalc/1.8.0/suncalc.min.js')
 .then(function(){
   let times = SunCalc.getTimes(new Date(), 51.5,-0.1);
   console.log("Golden Hour today in London: " + times.goldenHour.getHours() + ':' + times.sunrise.getMinutes() + ". Take your pics!")
  })
})()

https://caniuse.com/#feat=es6-module-dynamic-import

NVRM
fuente
1

Hay scripts diseñados específicamente para este propósito.

yepnope.js está integrado en Modernizr, y lab.js es una versión más optimizada (pero menos amigable para el usuario).

No recomendaría hacer esto a través de una gran biblioteca como jquery o prototipo, porque uno de los principales beneficios de un cargador de scripts es la capacidad de cargar scripts temprano, no debería tener que esperar hasta que jquery y todos sus elementos dom se carguen antes ejecutando una comprobación para ver si desea cargar dinámicamente un script.

1nfiniti
fuente
1

Escribí un módulo simple que automatiza el trabajo de importar / incluir scripts de módulos en JavaScript. Pruébalo y por favor ahorra algunos comentarios! :) Para obtener una explicación detallada del código, consulte esta publicación de blog: http://stamat.wordpress.com/2013/04/12/javascript-require-import-include-modules/

var _rmod = _rmod || {}; //require module namespace
_rmod.on_ready_fn_stack = [];
_rmod.libpath = '';
_rmod.imported = {};
_rmod.loading = {
    scripts: {},
    length: 0
};

_rmod.findScriptPath = function(script_name) {
    var script_elems = document.getElementsByTagName('script');
    for (var i = 0; i < script_elems.length; i++) {
        if (script_elems[i].src.endsWith(script_name)) {
            var href = window.location.href;
            href = href.substring(0, href.lastIndexOf('/'));
            var url = script_elems[i].src.substring(0, script_elems[i].length - script_name.length);
            return url.substring(href.length+1, url.length);
        }
    }
    return '';
};

_rmod.libpath = _rmod.findScriptPath('script.js'); //Path of your main script used to mark the root directory of your library, any library


_rmod.injectScript = function(script_name, uri, callback, prepare) {

    if(!prepare)
        prepare(script_name, uri);

    var script_elem = document.createElement('script');
    script_elem.type = 'text/javascript';
    script_elem.title = script_name;
    script_elem.src = uri;
    script_elem.async = true;
    script_elem.defer = false;

    if(!callback)
        script_elem.onload = function() {
            callback(script_name, uri);
        };

    document.getElementsByTagName('head')[0].appendChild(script_elem);
};

_rmod.requirePrepare = function(script_name, uri) {
    _rmod.loading.scripts[script_name] = uri;
    _rmod.loading.length++;
};

_rmod.requireCallback = function(script_name, uri) {
    _rmod.loading.length--;
    delete _rmod.loading.scripts[script_name];
    _rmod.imported[script_name] = uri;

    if(_rmod.loading.length == 0)
        _rmod.onReady();
};

_rmod.onReady = function() {
    if (!_rmod.LOADED) {
        for (var i = 0; i < _rmod.on_ready_fn_stack.length; i++){
            _rmod.on_ready_fn_stack[i]();
        });
        _rmod.LOADED = true;
    }
};

//you can rename based on your liking. I chose require, but it can be called include or anything else that is easy for you to remember or write, except import because it is reserved for future use.
var require = function(script_name) {
    var np = script_name.split('.');
    if (np[np.length-1] === '*') {
        np.pop();
        np.push('_all');
    }

    script_name = np.join('.');
    var uri = _rmod.libpath + np.join('/')+'.js';
    if (!_rmod.loading.scripts.hasOwnProperty(script_name) 
     && !_rmod.imported.hasOwnProperty(script_name)) {
        _rmod.injectScript(script_name, uri, 
            _rmod.requireCallback, 
                _rmod.requirePrepare);
    }
};

var ready = function(fn) {
    _rmod.on_ready_fn_stack.push(fn);
};

// ----- USAGE -----

require('ivar.util.array');
require('ivar.util.string');
require('ivar.net.*');

ready(function(){
    //do something when required scripts are loaded
});
stamat
fuente
1

Estoy perdido en todas estas muestras, pero hoy necesitaba cargar un .js externo desde mi .js principal e hice esto:

document.write("<script src='https://www.google.com/recaptcha/api.js'></script>");
adrianTNT
fuente
puedes echar un vistazo al enlace: respuesta
asmmahmud
1

Aquí hay uno simple con devolución de llamada y soporte de IE:

function loadScript(url, callback) {

    var script = document.createElement("script")
    script.type = "text/javascript";

    if (script.readyState) { //IE
        script.onreadystatechange = function () {
            if (script.readyState == "loaded" || script.readyState == "complete") {
                script.onreadystatechange = null;
                callback();
            }
        };
    } else { //Others
        script.onload = function () {
            callback();
        };
    }

    script.src = url;
    document.getElementsByTagName("head")[0].appendChild(script);
}

loadScript("https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function () {

     //jQuery loaded
     console.log('jquery loaded');

});
Jacob
fuente
1

Sé que mi respuesta es un poco tarde para esta pregunta, pero aquí hay un gran artículo en www.html5rocks.com : sumérgete en las aguas turbias de la carga de guiones .

En ese artículo se concluye que, en lo que respecta al soporte del navegador, la mejor manera de cargar dinámicamente el archivo JavaScript sin bloquear la representación del contenido es la siguiente:

Teniendo en cuenta que tiene cuatro scripts nombrados script1.js, script2.js, script3.js, script4.js, puede hacerlo aplicando async = false :

[
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
].forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.head.appendChild(script);
});

Ahora, Spec dice : Descargar juntos, ejecutar en orden tan pronto como se descargue todo.

Firefox <3.6, Opera dice: No tengo idea de qué es esta cosa "asíncrona", pero sucede que ejecuto scripts agregados a través de JS en el orden en que se agregaron.

Safari 5.0 dice: Entiendo "async", pero no entiendo configurarlo en "false" con JS. Ejecutaré tus scripts tan pronto como lleguen, en cualquier orden.

IE <10 dice: No tengo idea sobre "async", pero hay una solución alternativa usando "onreadystatechange".

Todo lo demás dice: soy tu amigo, vamos a hacer esto según el libro.

Ahora, el código completo con IE <10 solución alternativa:

var scripts = [
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
];
var src;
var script;
var pendingScripts = [];
var firstScript = document.scripts[0];

// Watch scripts load in IE
function stateChange() {
  // Execute as many scripts in order as we can
  var pendingScript;
  while (pendingScripts[0] && pendingScripts[0].readyState == 'loaded') {
    pendingScript = pendingScripts.shift();
    // avoid future loading events from this script (eg, if src changes)
    pendingScript.onreadystatechange = null;
    // can't just appendChild, old IE bug if element isn't closed
    firstScript.parentNode.insertBefore(pendingScript, firstScript);
  }
}

// loop through our script urls
while (src = scripts.shift()) {
  if ('async' in firstScript) { // modern browsers
    script = document.createElement('script');
    script.async = false;
    script.src = src;
    document.head.appendChild(script);
  }
  else if (firstScript.readyState) { // IE<10
    // create a script and add it to our todo pile
    script = document.createElement('script');
    pendingScripts.push(script);
    // listen for state changes
    script.onreadystatechange = stateChange;
    // must set src AFTER adding onreadystatechange listener
    // else we’ll miss the loaded event for cached scripts
    script.src = src;
  }
  else { // fall back to defer
    document.write('<script src="' + src + '" defer></'+'script>');
  }
}

Algunos trucos y minificación más tarde, son 362 bytes.

!function(e,t,r){function n(){for(;d[0]&&"loaded"==d[0][f];)c=d.shift(),c[o]=!i.parentNode.insertBefore(c,i)}for(var s,a,c,d=[],i=e.scripts[0],o="onreadystatechange",f="readyState";s=r.shift();)a=e.createElement(t),"async"in i?(a.async=!1,e.head.appendChild(a)):i[f]?(d.push(a),a[o]=n):e.write("<"+t+' src="'+s+'" defer></'+t+">"),a.src=s}(document,"script",[
  "//other-domain.com/1.js",
  "2.js"
])
asmmahmud
fuente
Estoy usando el enfoque anterior, funciona bien en Chrome y Firefox, pero enfrenta problemas en el navegador IE
Sr. Roshan, el
1

Algo como esto...

<script>
     $(document).ready(function() {
          $('body').append('<script src="https://maps.googleapis.com/maps/api/js?key=KEY&libraries=places&callback=getCurrentPickupLocation" async defer><\/script>');
     });
</script>
James Arnold
fuente
1

Aquí un ejemplo simple para una función para cargar archivos JS. Puntos relevantes:

  • no necesita jQuery, por lo que puede usar esto inicialmente para cargar también el archivo jQuery.js
  • es asíncrono con devolución de llamada
  • asegura que se cargue solo una vez, ya que mantiene un gabinete con el registro de las URL cargadas, evitando así el uso de la red
  • a diferencia de jQuery $.ajaxo $.getScriptpuede usar nonces, resolviendo así los problemas con CSP unsafe-inline. Solo usa la propiedadscript.nonce
var getScriptOnce = function() {

    var scriptArray = []; //array of urls (closure)

    //function to defer loading of script
    return function (url, callback){
        //the array doesn't have such url
        if (scriptArray.indexOf(url) === -1){

            var script=document.createElement('script');
            script.src=url;
            var head=document.getElementsByTagName('head')[0],
                done=false;

            script.onload=script.onreadystatechange = function(){
                if ( !done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete') ) {
                    done=true;
                    if (typeof callback === 'function') {
                        callback();
                    }
                    script.onload = script.onreadystatechange = null;
                    head.removeChild(script);

                    scriptArray.push(url);
                }
            };

            head.appendChild(script);
        }
    };
}();

Ahora lo usas simplemente por

getScriptOnce("url_of_your_JS_file.js");
João Pimentel Ferreira
fuente
1

Una frase absurda, para aquellos que piensan que cargar una biblioteca js no debería tomar más de una línea de código: P

await new Promise((resolve, reject) => {let js = document.createElement("script"); js.src="mylibrary.js"; js.onload=resolve; js.onerror=reject; document.body.appendChild(js)});

Obviamente, si el script que desea importar es un módulo, puede usar la import(...)función.


fuente
1

Con Promesas puedes simplificarlo así. Función del cargador:

  const loadCDN = src =>
    new Promise((resolve, reject) => {
      if (document.querySelector(`head > script[src="${src}"]`) !== null) return resolve()
      const script = document.createElement("script")
      script.src = src
      script.async = true
      document.head.appendChild(script)
      script.onload = resolve
      script.onerror = reject
    })

Uso (asíncrono / espera):

await loadCDN("https://.../script.js")

Uso (promesa):

loadCDN("https://.../script.js").then(res => {}).catch(err => {})

NOTA: había una solución similar pero no comprueba si el script ya está cargado y carga el script cada vez. Este comprueba la propiedad src.

radulle
fuente
0

Todas las principales bibliotecas de JavaScript como jscript, prototype, YUI tienen soporte para cargar archivos de script. Por ejemplo, en YUI, después de cargar el núcleo, puede hacer lo siguiente para cargar el control de calendario

var loader = new YAHOO.util.YUILoader({

    require: ['calendar'], // what components?

    base: '../../build/',//where do they live?

    //filter: "DEBUG",  //use debug versions (or apply some
                        //some other filter?

    //loadOptional: true, //load all optional dependencies?

    //onSuccess is the function that YUI Loader
    //should call when all components are successfully loaded.
    onSuccess: function() {
        //Once the YUI Calendar Control and dependencies are on
        //the page, we'll verify that our target container is 
        //available in the DOM and then instantiate a default
        //calendar into it:
        YAHOO.util.Event.onAvailable("calendar_container", function() {
            var myCal = new YAHOO.widget.Calendar("mycal_id", "calendar_container");
            myCal.render();
        })
     },

    // should a failure occur, the onFailure function will be executed
    onFailure: function(o) {
        alert("error: " + YAHOO.lang.dump(o));
    }

 });

// Calculate the dependency and insert the required scripts and css resources
// into the document
loader.insert();
Darren Kopp
fuente
puedes echar un vistazo al enlace: respuesta
asmmahmud
0

He modificado algunas de las publicaciones anteriores con un ejemplo de trabajo. Aquí podemos dar css y js en la misma matriz también.

$(document).ready(function(){

if (Array.prototype.contains === undefined) {
Array.prototype.contains = function (obj) {
    var i = this.length;
    while (i--) { if (this[i] === obj) return true; }
    return false;
};
};

/* define object that will wrap our logic */
var jsScriptCssLoader = {

jsExpr : new RegExp( "js$", "i" ),
cssExpr : new RegExp( "css$", "i" ),
loadedFiles: [],

loadFile: function (cssJsFileArray) {
    var self = this;
    // remove duplicates with in array
    cssJsFileArray.filter((item,index)=>cssJsFileArray.indexOf(item)==index)
    var loadedFileArray = this.loadedFiles;
    $.each(cssJsFileArray, function( index, url ) {
            // if multiple arrays are loaded the check the uniqueness
            if (loadedFileArray.contains(url)) return;
            if( self.jsExpr.test( url ) ){
                $.get(url, function(data) {
                    self.addScript(data);
                });

            }else if( self.cssExpr.test( url ) ){
                $.get(url, function(data) {
                    self.addCss(data);
                });
            }

            self.loadedFiles.push(url);
    });

    // don't load twice accross different arrays

},
addScript: function (code) {
    var oNew = document.createElement("script");
    oNew.type = "text/javascript";
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);
},
addCss: function (code) {
    var oNew = document.createElement("style");
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);
}

};


//jsScriptCssLoader.loadFile(["css/1.css","css/2.css","css/3.css"]);
jsScriptCssLoader.loadFile(["js/common/1.js","js/2.js","js/common/file/fileReader.js"]);
});
VG P
fuente
0

Para aquellos de ustedes que aman las frases sencillas:

import('./myscript.js');

Es probable que obtenga un error, como:

El acceso al script en ' http: //..../myscript.js ' desde el origen ' http://127.0.0.1 ' ha sido bloqueado por la política de CORS: no hay encabezado 'Access-Control-Allow-Origin' en El recurso solicitado.

En cuyo caso, puede recurrir a:

fetch('myscript.js').then(r => r.text()).then(t => new Function(t)());
Ludmil Tinkov
fuente