Carregar dinamicamente um arquivo JavaScript


168

Como você pode carregar de maneira confiável e dinâmica um arquivo JavaScript? Isso pode ser usado para implementar um módulo ou componente que, quando 'inicializado', o componente carregará dinamicamente todos os scripts de biblioteca JavaScript necessários sob demanda.

O cliente que usa o componente não é obrigado a carregar todos os arquivos de script da biblioteca (e inserir manualmente <script>tags em suas páginas da web) que implementam esse componente - apenas o arquivo de script do componente 'principal'.

Como as principais bibliotecas JavaScript conseguem isso (Prototype, jQuery, etc)? Essas ferramentas mesclam vários arquivos JavaScript em uma única versão redistribuível de 'build' de um arquivo de script? Ou eles fazem algum carregamento dinâmico de scripts auxiliares da 'biblioteca'?

Uma adição a esta pergunta: existe uma maneira de lidar com o evento depois que um arquivo JavaScript incluído dinamicamente é carregado? O protótipo possui document.observepara eventos em todo o documento. Exemplo:

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

Quais são os eventos disponíveis para um elemento de script?


Respostas:


84

Você pode escrever tags de script dinâmicas (usando Prototype ):

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

O problema aqui é que não sabemos quando o arquivo de script externo está totalmente carregado.

Muitas vezes queremos nosso código dependente na linha seguinte e gostamos de escrever algo como:

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

Existe uma maneira inteligente de injetar dependências de script sem a necessidade de retornos de chamada. Você simplesmente precisa extrair o script por meio de uma solicitação AJAX síncrona e avaliar o script em nível global.

Se você usar Prototype, o método Script.load será semelhante a este:

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

O que posso fazer para que funcione em vários domínios? (script de carregamento de http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570


@Ciastopiekarz: Eu não controlo web.archive.org.
precisa saber é o seguinte

Então você tem que raspar os dados que você deseja acessar usando algum outro programa e fornecê-lo para si mesmo
David Schumann

o que é Ajax.Request ??
bluejayke

72

Não há importação / inclusão / exigência em javascript, mas existem duas maneiras principais de conseguir o que você deseja:

1 - Você pode carregá-lo com uma chamada AJAX e usar eval.

Essa é a maneira mais direta, mas está limitada ao seu domínio devido às configurações de segurança do Javascript, e o uso do eval está abrindo as portas para bugs e hacks.

2 - Adicione uma tag de script com a URL do script no HTML.

Definitivamente o melhor caminho a percorrer. Você pode carregar o script mesmo de um servidor externo e ele fica limpo quando você usa o analisador de navegador para avaliar o código. Você pode colocar a tag no cabeçalho da página da web ou na parte inferior do corpo.

Ambas as soluções são discutidas e ilustradas aqui.

Agora, há um grande problema que você deve conhecer. Isso implica que você carrega o código remotamente. Navegadores modernos carregam o arquivo e continuam executando o script atual, porque carregam tudo de forma assíncrona para melhorar o desempenho.

Isso significa que, se você usar esses truques diretamente, não poderá usar seu código recém-carregado na próxima linha depois de solicitar que ele seja carregado, porque ainda estará carregando.

EG: my_lovely_script.js contém 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

Então você recarrega a página pressionando F5. E funciona! Confuso ...

Então o que fazer sobre isso ?

Bem, você pode usar o hack que o autor sugere no link que eu lhe dei. Em resumo, para pessoas com pressa, ele usa en event para executar uma função de retorno de chamada quando o script é carregado. Assim, você pode colocar todo o código usando a biblioteca remota na função de retorno de chamada. POR EXEMPLO :

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

Em seguida, você escreve o código que deseja usar APÓS o script ser carregado em uma função lambda:

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

Então você executa tudo isso:

loadScript("my_lovely_script.js", myPrettyCode);

OK, entendi. Mas é uma dor escrever todas essas coisas.

Bem, nesse caso, você pode usar como sempre a fantástica estrutura gratuita do jQuery, que permite fazer a mesma coisa em uma linha:

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

11
Não posso acreditar como esta resposta é subestimada. Obrigado.
naftalimich 12/07/2015

2
Aposto que é porque as pessoas não gostam de ler além da primeira linha, a menos que prometa que elas serão ricas se "apenas seguirem esses três passos secretos para o sucesso".
Costa

Isso funciona. Espero que compartilhar minha experiência aqui economize seu tempo, porque passei muitas horas nisso. Estou usando o Angular 6 e o ​​modelo aplicado (html, css, jquery) O problema é que o modelo tem um arquivo js que é carregado depois que os elementos html são carregados para anexar eventos de listeners. Foi difícil executar esse arquivo js após o carregamento do aplicativo angular. A inclusão na tag de scripts angular app (angular.json) os agrupará, mas não executará esse arquivo js após o carregamento. É muito código para reescrever em texto datilografado, então isso foi uma grande ajuda. No próximo comentário, colocarei o exemplo por causa da duração do comentário
Nour Lababidi 23/09

Simplesmente usei esse código da seguinte maneira: ngAfterViewInit () {debugger; $ .getScript ("/ assets / js / jquery.app.js", function () {alert ("Script carregado e executado."); // aqui você pode usar qualquer coisa que você definiu no script carregado}); }
Nour Lababidi 23/09/18

Para o '$' em angular, segui o seguinte: stackoverflow.com/questions/32050645/…
Nour Lababidi

28

Eu usei uma versão muito menos complicada recentemente com o 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>

Funcionou muito bem em todos os navegadores em que o testei: IE6 / 7, Firefox, Safari, Opera.

Atualização: versão sem 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>

25
Isso é ótimo ... a menos que você esteja tentando carregar o jquery.

1
Looks como jQuery vai rolar na Exigir plugin no Núcleo do jQuery para um futuro lançamento: plugins.jquery.com/project/require
Adam

1
Uma maneira ainda melhor de usar o jQuery é usar $.getScript. Veja minha resposta.
Muhd

1
Modernizr (yepnope.js) ou lab.js são as soluções apropriadas para isso. o uso de uma biblioteca de scripts pesada (que deve ser carregada primeiro) não é a resposta mais adequada para dispositivos móveis ou para muitas outras situações.
1nfiniti

2
Navegadores mais antigos e validadores de html do @MaulikGangani interpretariam isso como o token para finalizar o script.
Travis

20

Fiz basicamente a mesma coisa que você fez com Adam, mas com uma ligeira modificação para me certificar de que eu estava anexando à etiqueta para concluir o trabalho. Eu simplesmente criei uma função de inclusão (código abaixo) para lidar com arquivos de script e css.

Essa função também verifica se o script ou o arquivo CSS já não foi carregado dinamicamente. Ele não verifica os valores codificados manualmente e pode ter havido uma maneira melhor de fazer isso, mas serviu ao objetivo.

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

Sem mais contexto do que Pickle, receio não ter nenhuma sugestão. O código acima funciona como está, foi extraído diretamente de um projeto em funcionamento.
palehorse

5
@palehorse, Mike e Muhd, estão certos, pode funcionar no seu projeto, mas é porque as variáveis ​​"includedFile" e "Array" devem ser definidas em outras partes do seu projeto; esse código por si só não será executado; melhor para editá-lo para que ele possa trabalhar fora do contexto de seu projeto, ou pelo menos adicionar um comentário explicando o que essas variáveis indefinidas são (digite etc)
user280109

14

outra resposta incrível

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


2
O que posso fazer para que funcione em vários domínios? (script de carregamento de http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570

9

Aqui está um exemplo de código que eu encontrei ... alguém tem uma maneira melhor?

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

O que posso fazer para que funcione em vários domínios? (script de carregamento de http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570

6

Se você já possui o jQuery carregado, deve usar $ .getScript .

Isso tem uma vantagem sobre as outras respostas aqui, pois você possui uma função de retorno de chamada interna (para garantir que o script seja carregado antes da execução do código dependente) e pode controlar o cache.


4

Se você deseja que um script SYNC seja carregado, é necessário adicionar o texto do script diretamente à tag HTML HEAD. Adicioná-lo como acionará uma carga ASYNC . Para carregar o texto do script de um arquivo externo de forma síncrona, use XHR. Abaixo um exemplo rápido (ele usa partes de outras respostas nesta e em outras postagens):

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

3

alguém tem uma maneira melhor?

Eu acho que apenas adicionar o script ao corpo seria mais fácil do que adicioná-lo ao último nó da página. Que tal agora:

function include(url) {
  var s = document.createElement("script");
  s.setAttribute("type", "text/javascript");
  s.setAttribute("src", url);
  document.body.appendChild(s);
}

O que posso fazer para que funcione em vários domínios? (script de carregamento de http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570

3

Eu usei ainda outra solução que encontrei na rede ... esta está em creativecommons e verifica se a fonte foi incluída antes de chamar a função ...

você pode encontrar o arquivo aqui: include.js

/** include - including .js files from JS - bfults@gmail.com - 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);
    }
  }   
} 

O que posso fazer para que funcione em vários domínios? (script de carregamento de http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570

3

Acabei de descobrir um ótimo recurso no YUI 3 (no momento da redação deste artigo, disponível na versão prévia). Você pode inserir facilmente dependências nas bibliotecas YUI e nos módulos "externos" (o que você está procurando) sem muito código: YUI Loader .

Ele também responde à sua segunda pergunta sobre a função que está sendo chamada assim que o módulo externo é carregado.

Exemplo:

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

Funcionou perfeitamente para mim e tem a vantagem de gerenciar dependências. Consulte a documentação da YUI para obter um exemplo com o calendário da YUI 2 .


Provavelmente, isso é ideal, exceto que o YUI é obscenamente grande apenas para essa funcionalidade.
Muhd

3

Há um novo padrão proposto para ECMA chamado importação dinâmica , recentemente incorporado ao Chrome e Safari.

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

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

2

A técnica que usamos no trabalho é solicitar o arquivo javascript usando uma solicitação AJAX e, em seguida, eval () o retorno. Se você estiver usando a biblioteca de protótipos, eles suportam essa funcionalidade na chamada Ajax.Request.


2

O jquery resolveu isso para mim com sua função .append () - usada para carregar o pacote completo da interface do usuário do jquery

/*
 * 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 - na cabeça do seu html / php / etc depois de importar o jquery.js, você incluiria apenas esse arquivo para carregar na totalidade da sua biblioteca, anexando-o à cabeça ...

<script type="text/javascript" src="project.library.js"></script>

2

Mantenha-o agradável, curto, simples e sustentável! :]

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

Esse código é simplesmente um exemplo funcional curto que pode exigir funcionalidade de recurso adicional para suporte total em qualquer plataforma (ou fornecida).


2

A importação de módulo dinâmico chegou ao Firefox 67+ .

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

Com tratamento de erros:

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

Também funciona para qualquer tipo de biblioteca que não seja um módulo, neste caso a lib está disponível no objeto window, da maneira antiga, mas somente sob demanda, o que é bom.

Exemplo usando suncalc.js , o servidor deve ter o CORS ativado para funcionar dessa maneira!

(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


1

Existem scripts projetados especificamente para esse fim.

O yepnope.js está embutido no Modernizr e o lab.js é uma versão mais otimizada (mas menos amigável).

Eu não recomendaria fazer isso por meio de uma grande biblioteca como jquery ou protótipo - porque um dos principais benefícios de um carregador de scripts é a capacidade de carregar scripts mais cedo - você não deve esperar até que o jquery e todos os seus elementos dom sejam carregados antes executando uma verificação para ver se você deseja carregar dinamicamente um script.


1

Escrevi um módulo simples que automatiza o trabalho de importar / incluir scripts de módulo em JavaScript. Experimente e poupe algum feedback! :) Para obter uma explicação detalhada do código, consulte esta postagem do 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
});

1

Estou perdido em todos esses exemplos, mas hoje eu precisava carregar um .js externo do meu .js principal e fiz isso:

document.write("<script src='https://www.google.com/recaptcha/api.js'></script>");

você pode dar uma olhada no link: resposta
asmmahmud 3/17/17

1

Aqui está um simples, com retorno de chamada e suporte ao 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');

});

1

Sei que minha resposta está um pouco atrasada para esta pergunta, mas aqui está um ótimo artigo em www.html5rocks.com - Mergulhe fundo nas águas turvas do carregamento de scripts .

Nesse artigo, conclui-se que, em relação ao suporte ao navegador, a melhor maneira de carregar dinamicamente o arquivo JavaScript sem bloquear a renderização do conteúdo é a seguinte:

Considerando que você tem quatro scripts nomeados script1.js, script2.js, script3.js, script4.js, pode fazê-lo com a aplicação de 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);
});

Agora, Spec diz : Faça o download em conjunto, execute em ordem assim que o download for feito.

Firefox <3.6, o Opera diz: Não tenho idéia do que é essa coisa de "assíncrono", mas acontece que executo scripts adicionados via JS na ordem em que são adicionados.

O Safari 5.0 diz: Eu entendo "assíncrono", mas não entendo configurá-lo para "falso" com JS. Executarei seus scripts assim que eles chegarem, em qualquer ordem.

O IE <10 diz: Nenhuma idéia sobre "assíncrono", mas existe uma solução alternativa usando "onreadystatechange".

Tudo o resto diz: Eu sou seu amigo, vamos fazer isso conforme as regras.

Agora, o código completo com o IE <10 solução 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>');
  }
}

Alguns truques e minificação depois, são 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"
])

Eu estou usando acima abordagem é muito bem em cromo & firefox trabalhando, mas enfrenta problemas no navegador IE
Roshan

1

Algo assim...

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

1

Aqui está um exemplo simples para uma função carregar arquivos JS. Pontos relevantes:

  • você não precisa do jQuery, portanto, você pode usá-lo inicialmente para carregar também o arquivo jQuery.js
  • é assíncrono com retorno de chamada
  • garante que ele seja carregado apenas uma vez, pois mantém um gabinete com o registro de URLs carregados, evitando o uso da rede
  • contrário ao jQuery $.ajaxou $.getScriptvocê pode usar nonces, resolvendo problemas com o CSP unsafe-inline. Basta usar a propriedadescript.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);
        }
    };
}();

Agora você o usa simplesmente

getScriptOnce("url_of_your_JS_file.js");

1

Um one-liner absurdo, para aqueles que pensam que carregar uma biblioteca js não deve ter mais de uma linha 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, se o script que você deseja importar for um módulo, você poderá usar a import(...)função


1

Com o Promises, você pode simplificar assim. Função carregador:

  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 (assíncrono / aguardar):

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

Uso (Promessa):

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

NOTA: havia uma solução semelhante, mas ela não verifica se o script já está carregado e carrega o script a cada vez. Este verifica a propriedade src.


0

todas as principais bibliotecas javascript como jscript, prototype e YUI têm suporte para carregar arquivos de script. Por exemplo, na YUI, depois de carregar o núcleo, você pode fazer o seguinte para carregar o controle de calendário

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

você pode dar uma olhada no link: resposta
asmmahmud 3/17/17

0

Eu ajustei algumas das postagens acima com o exemplo de trabalho. Aqui também podemos fornecer css e js na mesma matriz.

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

0

Para aqueles de vocês que amam one-liners:

import('./myscript.js');

Provavelmente, você pode receber um erro, como:

O acesso ao script em ' http: //..../myscript.js ' da origem ' http://127.0.0.1 ' foi bloqueado pela política do CORS: Nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no o recurso solicitado.

Nesse caso, você pode fazer o fallback para:

fetch('myscript.js').then(r => r.text()).then(t => new Function(t)());
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.