Как загружать и использовать/вызывать динамически JavaScript

Мне нужно динамически загружать файл JavaScript, а затем обращаться к его контенту.

Файл test.js

test = function () {
    var pub = {}
    pub.defult_id = 1;
    return pub;
}()


В этом случае он работает:

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="/test.js"></script>    
</head>
<body>
    <script type="text/javascript">
        console.log(test.defult_id);
    </script>
</body>
</html>


Но мне нужно загрузить его динамически, и таким образом он не работает:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    <script type="text/javascript">
        function loadjs(file) {
            var script = document.createElement("script");
            script.type = "application/javascript";
            script.src = file;
            document.body.appendChild(script);
        }
        loadjs('test.js');
        console.log(test.defult_id);
    </script>
</body>
</html>


Ошибка: Uncaught ReferenceError: test is not defined(…)

Ответ 1

Вы можете сделать это так:

function loadjs(file) {
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = file;
    script.onload = function(){
        alert("Script is ready!"); 
        console.log(test.defult_id);
    };
    document.body.appendChild(script);
 }

Для получения дополнительной информации прочитайте эту статью: https://www.nczonline.net/blog/2009/06/23/loading-javascript-without-blocking/

Ответ 2

Есть отличная статья, которую стоит прочитать всем парням, интересующимся загрузкой js-скриптов, в www.html5rocks.com - Погрузитесь в темные воды загрузки скриптов.

В этой статье, рассмотрев множество возможных решений, автор пришел к выводу, что добавление js-скриптов в конец элемента body является наилучшим из возможных способов избежать блокирования рендеринга страницы js-скриптами, тем самым ускоряя время загрузки страницы.

Но автор предлагает другое хорошее альтернативное решение для тех людей, которые отчаянно пытаются загружать и выполнять сценарии асинхронно.

Учитывая, что у вас есть четыре сценария с именем script1.js, script2.js, script3.js, script4.js, вы можете сделать это с помощью применения 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);
});

Теперь Спец говорит: скачать вместе, выполнить по порядку, как только все загрузят.

Firefox & lt; 3.6, Opera говорит: Я понятия не имею, что это за асинхронная вещь, но так получилось, что я выполняю сценарии, добавленные через JS, в порядке их добавления.

Safari 5.0 говорит: Я понимаю 'async', но не понимаю, как установить его в 'false' с JS. Я выполню ваши сценарии, как только они приземлятся, в любом порядке.

IE & lt; 10 говорит: Нет понятия об "асинхронности", но есть обходной путь, использующий "onreadystatechange".

Все остальное говорит: Я твой друг, собирался сделать это по книге.

Теперь полный код с IE & lt; 10 обходных путей:

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 well miss the loaded event for cached scripts
    script.src = src;
  }
  else { // fall back to defer
    document.write('<script src="' + src + '" defer></'+'script>');
  }
}

Несколько трюков и минимизация позже, его 362 байта

!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"
])

Ответ 3

Динамическая загрузка JS файлов является асинхронной, поэтому, чтобы обеспечить загрузку script перед вызовом некоторой функции внутри, используйте событие onload в script:

function loadjs(file) {
            var script = document.createElement("script");
            script.type = "application/javascript";
            script.onload=function(){
                //at this tine the script is loaded
                console.log("Script loaded!");
                console.log(test);
            }
            script.src = file;
            document.body.appendChild(script);
        }