Javascript document.createElement("script") 同步

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3248384/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 03:52:32  来源:igfitidea点击:

document.createElement("script") synchronously

javascriptdomsynchronous

提问by Josh Johnson

Is it possible to call in a .jsfile synchronously and then use it immediately afterward?

是否可以.js同步调用文件然后立即使用它?

<script type="text/javascript">
    var head = document.getElementsByTagName('head').item(0);
    var script = document.createElement('script');
    script.setAttribute('type', 'text/javascript');
    script.setAttribute('src', 'http://mysite/my.js');
    head.appendChild(script);

    myFunction(); // Fails because it hasn't loaded from my.js yet.

    window.onload = function() {
        // Works most of the time but not all of the time.
        // Especially if my.js injects another script that contains myFunction().
        myFunction();
    };
</script>

This is simplified. In my implementation the createElement stuff is in a function. I thought about adding something to the function that could check to see if a certain variable was instantiated before returning control. But then there is still the problem of what to do when including js from another site that I have no control over.

这是简化的。在我的实现中,createElement 的东西在一个函数中。我想在函数中添加一些可以检查某个变量是否在返回控制之前实例化的东西。但是当包含来自我无法控制的另一个站点的 js 时,仍然存在问题。

Thoughts?

想法?

Edit:

编辑:

I've accepted the best answer for now because it gives a good explanation for what's going on. But if anyone has any suggestions for how to improve this I'm open to them. Here's an example of what I'd like to do.

我现在接受了最好的答案,因为它很好地解释了正在发生的事情。但是,如果有人对如何改进这一点有任何建议,我对他们持开放态度。这是我想做的一个例子。

// Include() is a custom function to import js.
Include('my1.js');
Include('my2.js');

myFunc1('blarg');
myFunc2('bleet');

I just want to keep from having to know the internals too much and just be able to say, "I wish to use this module, and now I will use some code from it."

我只是不想过多地了解内部结构,而只想说:“我希望使用这个模块,现在我将使用它的一些代码。”

回答by Pointy

You can create your <script>element with an "onload" handler, and that will be called when the script has been loaded and evaluated by the browser.

您可以<script>使用“onload”处理程序创建元素,该处理程序将在浏览器加载和评估脚本时调用。

var script = document.createElement('script');
script.onload = function() {
  alert("Script loaded and ready");
};
script.src = "http://whatever.com/the/script.js";
document.getElementsByTagName('head')[0].appendChild(script);

You can't do it synchronously.

你不能同步进行。

edit— it's been pointed out that, true to form, IE doesn't fire a "load" event on <script>tags being loaded/evaluated. Thus I suppose the next thing to do would be to fetch the script with an XMLHttpRequest and then eval()it yourself. (Or, I suppose, stuff the text into a <script>tag you add; the execution environment of eval()is affected by the local scope, so it won't necessarily do what you want it to do.)

编辑- 有人指出,正如形式一样,IE 不会<script>在加载/评估标签时触发“加载”事件。因此,我想接下来要做的是使用 XMLHttpRequest 获取脚本,然后eval()自己获取。(或者,我想,将文本填充到<script>您添加的标签中; 的执行环境eval()受本地范围的影响,因此它不一定会执行您希望它执行的操作。)

editAs of early 2013, I'd strongly advise looking into a more robust script loading tool like Requirejs. There are a lot of special cases to worry about. For really simple situations, there's yepnope, which is now built into Modernizr.

编辑-截至 2013 年初,我强烈建议寻找更强大的脚本加载工具,如Requirejs。有很多特殊情况需要担心。对于非常简单的情况,有yepnope,它现在内置在Modernizr 中

回答by Josh Johnson

This isn't pretty, but it works:

这并不漂亮,但它有效:

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"></script>');
</script>

<script type="text/javascript">
  functionFromOther();
</script>

Or

或者

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"></script>');
  window.onload = function() {
    functionFromOther();
  };
</script>

The script must be included either in a separate <script>tag or before window.onload().

脚本必须包含在单独的<script>标记中或之前window.onload()

This will not work:

这将不起作用:

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"></script>');
  functionFromOther(); // Error
</script>

The same can be done with creating a node, as Pointy did, but only in FF. You have no guarantee when the script will be ready in other browsers.

创建节点也可以这样做,就像 Pointy 所做的那样,但仅限于 FF。您无法保证脚本何时可以在其他浏览器中就绪。

Being an XML Purist I really hate this. But it does work predictably. You could easily wrap those ugly document.write()s so you don't have to look at them. You could even do tests and create a node and append it then fall back on document.write().

作为一个 XML 纯粹主义者,我真的很讨厌这个。但它确实可以预见。您可以轻松地包装那些丑陋的document.write()s,这样您就不必看它们了。您甚至可以进行测试并创建一个节点并将其附加到document.write().

回答by zcourts

This is way late but for future reference to anyone who'd like to do this, you can use the following:

这已经很晚了,但为了将来供任何想要这样做的人参考,您可以使用以下内容:

function require(file,callback){
    var head=document.getElementsByTagName("head")[0];
    var script=document.createElement('script');
    script.src=file;
    script.type='text/javascript';
    //real browsers
    script.onload=callback;
    //Internet explorer
    script.onreadystatechange = function() {
        if (this.readyState == 'complete') {
            callback();
        }
    }
    head.appendChild(script);
}

I did a short blog post on it some time ago http://crlog.info/2011/10/06/dynamically-requireinclude-a-javascript-file-into-a-page-and-be-notified-when-its-loaded/

前段时间我写了一篇简短的博客文章http://crlog.info/2011/10/06/dynamically-requireinclude-a-javascript-file-into-a-page-and-be-notified-when-its -加载/

回答by gblazex

Asynchronousprogramming is slightly more complicatedbecause the consequence of making a request is encapsulated in a function instead of following the request statement. Butthe realtime behavior that the user experiencescan be significantly betterbecause they will not see a sluggish server or sluggish network cause the browser to act as though it had crashed. Synchronousprogramming is disrespectfuland should not be employedin applications which are used by people.

异步编程稍微复杂一些,因为发出请求的结果被封装在一个函数中,而不是遵循请求语句。但是用户体验的实时行为可能会明显更好,因为他们不会看到缓慢的服务器或缓慢的网络导致浏览器表现得好像它已经崩溃。同步编程是不尊重的不应在人们使用的应用程序中使用。

Douglas Crockford(YUI Blog)

道格拉斯·克罗克福德YUI 博客

Alright, buckle your seats, because it's going to be a bumpy ride. More and more people ask about loading scripts dynamically via javascript, it seems to be a hot topic.

好吧,扣好你的座位,因为这将是一段颠簸的旅程。越来越多的人询问通过javascript动态加载脚本,这似乎是一个热门话题。

The main reasons why this became so popular are:

它变得如此受欢迎的主要原因是:

  • client-side modularity
  • easier dependency management
  • error handling
  • performance advantages
  • 客户端模块化
  • 更容易的依赖管理
  • 错误处理
  • 性能优势

About modularity: it is obvious that managing client-side dependencies should be handled right on the client-side. If a certain object, module or library is needed we just ask for it and load it dynamically.

关于模块化:很明显,管理客户端依赖项应该在客户端正确处理。如果需要某个对象、模块或库,我们只需请求它并动态加载它。

Error handling: if a resource fails we still get the chance to block only the parts that depend on the affected script, or maybe even give it another try with some delay.

错误处理:如果资源失败,我们仍然有机会仅阻止依赖于受影响脚本的部分,或者甚至可能会延迟一段时间再试一次。

Performancehas become a competitive edge between websites, it is now a search ranking factor. What dynamic scripts can do is mimic asynchronous behavior as opposed to the default blocking way of how browsers handle scripts. Scripts blockother resources, scripts blockfurther parsing of the HTML document, scripts blockthe UI. Now with dynamic script tags and its cross-browser alternatives you can do real asynchronous requests, and execute dependent code only when they are available. Your scripts will load in-parallel even with other resources and the rendering will be flawless.

性能已经成为网站之间的竞争优势,它现在是一个搜索排名因素。动态脚本可以做的是模仿异步行为,而不是浏览器处理脚本的默认阻塞方式。脚本阻止其他资源,脚本阻止进一步解析 HTML 文档,脚本阻止UI。现在使用动态脚本标签及其跨浏览器替代方案,您可以执行真正的异步请求,并仅在它们可用时执行相关代码。即使使用其他资源,您的脚本也将并行加载,并且渲染将完美无缺。

The reason why some people stick to synchronous scripting is because they are used to it. They think it is the default way, it is the easier way, and some may even think it is the only way.

有些人之所以坚持同步脚本,是因为他们已经习惯了。他们认为这是默认的方式,这是更简单的方式,有些人甚至认为这是唯一的方式。

But the only thing we should care about when this needs to be decided concerning an applications's design is the end-user experience. And in this area asynchronous cannot be beaten. The user gets immediate responses(or say promises), and a promise is always better than nothing. A blank screen scares people. Developers shouldn't be lazy to enhance perceived performance.

但是,当需要就应用程序的设计做出决定时,我们唯一应该关心的是最终用户体验。在这个领域异步是不可战胜的。用户立即得到响应(或说承诺),承诺总比没有好。一个空白的屏幕会吓到人们。开发人员不应该懒于提高感知性能

And finally some words about the dirty side. What you should do in order to get it working across browsers:

最后是关于肮脏的一面。为了让它在浏览器中工作,你应该做什么:

  1. learn to think asynchronously
  2. organize your code to be modular
  3. organize your code to handle errors and edge cases well
  4. enhance progressively
  5. always take care of the right amount of feedback
  1. 学会异步思考
  2. 将您的代码组织成模块化
  3. 组织您的代码以很好地处理错误和边缘情况
  4. 逐步提高
  5. 始终注意适当数量的反馈

回答by James

The answers above pointed me in the right direction. Here is a generic version of what I got working:

上面的答案为我指明了正确的方向。这是我工作的通用版本:

  var script = document.createElement('script');
  script.src = 'http://' + location.hostname + '/module';
  script.addEventListener('load', postLoadFunction);
  document.head.appendChild(script);

  function postLoadFunction() {
     // add module dependent code here
  }      

回答by Daggie Blanqx - Douglas Mwangi

function include(file){
return new Promise(function(resolve, reject){
        var script = document.createElement('script');
        script.src = file;
        script.type ='text/javascript';
        script.defer = true;
        document.getElementsByTagName('head').item(0).appendChild(script);

        script.onload = function(){
        resolve()
        }
        script.onerror = function(){
          reject()
        }
      })

 /*I HAVE MODIFIED THIS TO  BE PROMISE-BASED 
   HOW TO USE THIS FUNCTION 

  include('js/somefile.js').then(function(){
  console.log('loaded');
  },function(){
  console.log('not loaded');
  })
  */
}

回答by jeremykentbgross

I had the following problem(s) with the existing answers to this question (and variations of this question on other stackoverflow threads):

我在这个问题的现有答案中遇到了以下问题(以及这个问题在其他 stackoverflow 线程上的变体):

  • None of the loaded code was debuggable
  • Many of the solutions required callbacks to know when loading was finished instead of truly blocking, meaning I would get execution errors from immediately calling loaded (ie loading) code.
  • 没有一个加载的代码是可调试的
  • 许多解决方案需要回调来知道加载何时完成而不是真正阻塞,这意味着我会从立即调用加载(即加载)代码中得到执行错误。

Or, slightly more accurately:

或者,稍微准确一点:

  • None of the loaded code was debuggable (except from the HTML script tag block, if and only if the solution added a script elements to the dom, and never ever as individual viewable scripts.)=> Given how many scripts I have to load (and debug), this was unacceptable.
  • Solutions using 'onreadystatechange' or 'onload' events failed to block, which was a big problem since the code originally loaded dynamic scripts synchronously using 'require([filename, 'dojo/domReady']);' and I was stripping out dojo.
  • 加载的代码都不是可调试的(除了 HTML 脚本标记块,当且仅当解决方案向 dom 添加了脚本元素,并且永远不会作为单独的可见脚本。)=> 鉴于我必须加载多少脚本(和调试),这是不可接受的。
  • 使用 'onreadystatechange' 或 'onload' 事件的解决方案未能阻止,这是一个大问题,因为代码最初使用 'require([filename, 'dojo/domReady']);' 同步加载动态脚本。我正在剥离道场。

My final solution, which loads the script before returning, AND has all scripts properly accessible in the debugger (for Chrome at least) is as follows:

我的最终解决方案,在返回之前加载脚本,并在调试器中正确访问所有脚本(至少对于 Chrome)如下:

WARNING: The following code should PROBABLY be used only in 'development' mode.(For 'release' mode I recommend prepackaging and minification WITHOUT dynamic script loading, or at least without eval).

警告:以下代码可能仅用于“开发”模式。(对于“发布”模式,我建议在不加载动态脚本或至少不加载 eval 的情况下进行预打包和缩小)。

//Code User TODO: you must create and set your own 'noEval' variable

require = function require(inFileName)
{
    var aRequest
        ,aScript
        ,aScriptSource
        ;

    //setup the full relative filename
    inFileName = 
        window.location.protocol + '//'
        + window.location.host + '/'
        + inFileName;

    //synchronously get the code
    aRequest = new XMLHttpRequest();
    aRequest.open('GET', inFileName, false);
    aRequest.send();

    //set the returned script text while adding special comment to auto include in debugger source listing:
    aScriptSource = aRequest.responseText + '\n////# sourceURL=' + inFileName + '\n';

    if(noEval)//<== **TODO: Provide + set condition variable yourself!!!!**
    {
        //create a dom element to hold the code
        aScript = document.createElement('script');
        aScript.type = 'text/javascript';

        //set the script tag text, including the debugger id at the end!!
        aScript.text = aScriptSource;

        //append the code to the dom
        document.getElementsByTagName('body')[0].appendChild(aScript);
    }
    else
    {
        eval(aScriptSource);
    }
};

回答by morgancodes

This looks like a decent overview of dynamic script loading: http://unixpapa.com/js/dyna.html

这看起来像是动态脚本加载的一个不错的概述:http: //unixpapa.com/js/dyna.html

回答by user1251840

I am used to having multiple .js files on my web site that depend one on another. To load them and ensure that the dependencies are evaluated in the right order, I have written a function that loads all the files and then, once they are all received, eval()them. The main drawback is that since this does not work with CDN. For such libraries (e.g., jQuery) it is better to include them statically. Note that inserting script nodes in the HTML dynamicallywon't guarantee that scripts are evaluated in the right order, at least not in Chrome (this was the major reason for writing this function).

我习惯于在我的网站上有多个 .js 文件,它们相互依赖。为了加载它们并确保以正确的顺序评估依赖项,我编写了一个函数来加载所有文件,然后在收到所有文件后加载eval()它们。主要缺点是因为这不适用于 CDN。对于此类库(例如 jQuery),最好静态地包含它们。请注意,在 HTML 中动态插入脚本节点并不能保证以正确的顺序评估脚本,至少在 Chrome 中不会(这是编写此函数的主要原因)。

function xhrs(reqs) {
  var requests = [] , count = [] , callback ;

  callback = function (r,c,i) {
    return function () {
      if  ( this.readyState == 4 ) {
        if (this.status != 200 ) {
          r[i]['resp']="" ;
        } 
        else {
          r[i]['resp']= this.responseText ;
        }
        c[0] = c[0] - 1 ;
        if ( c[0] == 0 ) {
          for ( var j = 0 ; j < r.length ; j++ ) {
            eval(r[j]['resp']) ;
          }
        }
      }
    }
  } ;
  if ( Object.prototype.toString.call( reqs ) === '[object Array]' ) {
    requests.length = reqs.length ;
  }
  else {
    requests.length = 1 ;
    reqs = [].concat(reqs);
  }
  count[0] = requests.length ;
  for ( var i = 0 ; i < requests.length ; i++ ) {
    requests[i] = {} ;
    requests[i]['xhr'] = new XMLHttpRequest () ;
    requests[i]['xhr'].open('GET', reqs[i]) ;
    requests[i]['xhr'].onreadystatechange = callback(requests,count,i) ;
    requests[i]['xhr'].send(null);
  }
}

I haven't figured out how to make references to the same value without creating an array (for count). Otherwise I think it is self-explanatory (when everything is loaded, eval()every file in the order given, otherwise just store the response).

我还没有弄清楚如何在不创建数组的情况下引用相同的值(用于计数)。否则我认为它是不言自明的(加载eval()所有内容时,按给定顺序排列每个文件,否则只存储响应)。

Usage example:

用法示例:

xhrs( [
       root + '/global.js' ,
       window.location.href + 'config.js' ,
       root + '/js/lib/details.polyfill.min.js',
       root + '/js/scripts/address.js' ,
       root + '/js/scripts/tableofcontents.js' 
]) ;

回答by puk

Ironically, I have what you want, but want something closer to what you had.

具有讽刺意味的是,我拥有你想要的东西,但想要更接近你拥有的东西。

I am loading things in dynamically and asynchronously, but with an loadcallback like so (using dojo and xmlhtpprequest)

我正在动态和异步地加载东西,但是有一个load像这样的回调(使用 dojo 和 xmlhtpprequest)

  dojo.xhrGet({
    url: 'getCode.php',
    handleAs: "javascript",
    content : {
    module : 'my.js'
  },
  load: function() {
    myFunc1('blarg');
  },
  error: function(errorMessage) {
    console.error(errorMessage);
  }
});

For a more detailed explanation, see here

有关更详细的解释,请参见此处

The problem is that somewhere along the line the code gets evaled, and if there's anything wrong with your code, the console.error(errorMessage);statement will indicate the line where eval()is, not the actual error. This is SUCH a big problem that I am actually trying to convert back to <script>statements (see here.

问题是代码会在代码行的某个地方进行评估,如果您的代码有任何问题,该console.error(errorMessage);语句将指示该行在哪里eval(),而不是实际错误。这是一个大问题,我实际上正试图将其转换回<script>语句(请参阅此处的.