【问题标题】:Dynamically add script tag with src that may include document.write使用可能包含 document.write 的 src 动态添加脚本标签
【发布时间】:2022-01-06 15:36:21
【问题描述】:

我想在网页中动态包含一个脚本标签,但是我无法控制它的 src,所以 src="source.js" 可能看起来像这样。

document.write('<script type="text/javascript">')
document.write('alert("hello world")')
document.write('</script>')
document.write('<p>goodbye world</p>')

现在通常放

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

在头部工作正常,但有没有其他方法可以使用 innerHTML 之类的东西动态添加 source.js

jsfiddle of what i've tried

【问题讨论】:

标签: javascript src document.write


【解决方案1】:
var my_awesome_script = document.createElement('script');

my_awesome_script.setAttribute('src','http://example.com/site.js');

document.head.appendChild(my_awesome_script);

【讨论】:

    【解决方案2】:

    您可以像这样使用document.createElement() 函数:

    function addScript( src ) {
      var s = document.createElement( 'script' );
      s.setAttribute( 'src', src );
      document.body.appendChild( s );
    }
    

    【讨论】:

    • 你如何使这个异步?
    • @mobile 家伙:s.async = true;
    • s.setAttribute('src', src);可能是 s.src = src(我认为?)我知道这不是 pcg
    【解决方案3】:

    onload函数,脚本加载成功后可以调用:

    function addScript( src, callback ) {
      var s = document.createElement( 'script' );
      s.setAttribute( 'src', src );
      s.onload=callback;
      document.body.appendChild( s );
    }
    

    【讨论】:

    • 学到了一些新东西。不错!
    【解决方案4】:

    我为加载多个脚本而编写的一个不错的小脚本:

    function scriptLoader(scripts, callback) {
    
        var count = scripts.length;
    
        function urlCallback(url) {
            return function () {
                console.log(url + ' was loaded (' + --count + ' more scripts remaining).');
                if (count < 1) {
                    callback();
                }
            };
        }
    
        function loadScript(url) {
            var s = document.createElement('script');
            s.setAttribute('src', url);
            s.onload = urlCallback(url);
            document.head.appendChild(s);
        }
    
        for (var script of scripts) {
            loadScript(script);
        }
    };
    

    用法:

    scriptLoader(['a.js','b.js'], function() {
        // use code from a.js or b.js
    });
    

    【讨论】:

    【解决方案5】:

    这样做的唯一方法是将document.write 替换为您自己的函数,该函数会将元素附加到页面底部。使用 jQuery 非常简单:

    document.write = function(htmlToWrite) {
      $(htmlToWrite).appendTo('body');
    }
    

    如果您有 html 像问题示例一样以块的形式进入 document.write,您需要缓冲 htmlToWrite 段。也许是这样的:

    document.write = (function() {
      var buffer = "";
      var timer;
      return function(htmlPieceToWrite) {
        buffer += htmlPieceToWrite;
        clearTimeout(timer);
        timer = setTimeout(function() {
          $(buffer).appendTo('body');
          buffer = "";
        }, 0)
      }
    })()
    

    【讨论】:

      【解决方案6】:

      你可以试试下面的代码sn-p。

      function addScript(attribute, text, callback) {
          var s = document.createElement('script');
          for (var attr in attribute) {
              s.setAttribute(attr, attribute[attr] ? attribute[attr] : null)
          }
          s.innerHTML = text;
          s.onload = callback;
          document.body.appendChild(s);
      }
      
      addScript({
          src: 'https://www.google.com',
          type: 'text/javascript',
          async: null
      }, '<div>innerHTML</div>', function(){});
      

      【讨论】:

        【解决方案7】:

        嗯,有多种方法可以包含动态 javascript, 我在很多项目中都使用了这个。

        var script = document.createElement("script")
        script.type = "text/javascript";
        //Chrome,Firefox, Opera, Safari 3+
        script.onload = function(){
        console.log("Script is loaded");
        };
        script.src = "file1.js";
        document.getElementsByTagName("head")[0].appendChild(script);
        

        您可以调用 create 一个通用函数,它可以帮助您根据需要加载尽可能多的 javascript 文件。这里有一个完整的教程。

        Inserting Dynamic Javascript the right way

        【讨论】:

        • fyi,链接失效,我在别处找不到这篇文章。
        【解决方案8】:

        我尝试了递归地附加每个脚本

        注意如果您的脚本一个接一个地依赖,那么位置将需要同步。

        主要依赖项应该在数组的最后,以便初始脚本可以使用它

        const scripts = ['https://www.gstatic.com/firebasejs/6.2.0/firebase-storage.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-firestore.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-app.js']
        let count = 0
        
          
         const recursivelyAddScript = (script, cb) => {
          const el = document.createElement('script')
          el.src = script
          if(count < scripts.length) {
            count ++
            el.onload = recursivelyAddScript(scripts[count])
            document.body.appendChild(el)
          } else {
            console.log('All script loaded')
            return
          }
        }
         
          recursivelyAddScript(scripts[count])

        【讨论】:

          【解决方案9】:

          这是一个缩小的 sn-p,与 Google Analytics 和 Facebook Pixel 使用的代码相同:

          !function(e,s,t){(t=e.createElement(s)).async=!0,t.src="https://example.com/foo.js",(e=e.getElementsByTagName(s)[0]).parentNode.insertBefore(t,e)}(document,"script");
          

          https://example.com/foo.js 替换为您的脚本路径。

          【讨论】:

            【解决方案10】:

            这是为我工作的。

            你可以检查一下。

            var script_tag = document.createElement('script');
            script_tag.setAttribute('src','https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js');
            document.head.appendChild(script_tag);
            window.onload = function() {
                if (window.jQuery) {  
                    // jQuery is loaded  
                    alert("ADD SCRIPT TAG ON HEAD!");
                } else {
                    // jQuery is not loaded
                    alert("DOESN'T ADD SCRIPT TAG ON HEAD");
                }
            }

            【讨论】:

              【解决方案11】:

              当脚本被异步加载时,它们不能调用 document.write。这些调用将被忽略,并且将向控制台写入警告。

              您可以使用以下代码动态加载脚本:

              var scriptElm = document.createElement('script');
              scriptElm.src = 'source.js';
              document.body.appendChild(scriptElm);
              

              仅当您的源属于单独的文件时,此方法才有效。

              但是如果你有源代码作为你想要动态加载的内联函数并且想要将其他属性添加到脚本标签,例如类,类型等,那么下面的 sn-p 会帮助你:

              var scriptElm = document.createElement('script');
              scriptElm.setAttribute('class', 'class-name');
              var inlineCode = document.createTextNode('alert("hello world")');
              scriptElm.appendChild(inlineCode); 
              document.body.appendChild(scriptElm);
              

              【讨论】:

              • target 是什么?
              • @Gel,更新了答案,大多数情况下目标是document.body
              【解决方案12】:

              单行(不过与上述答案没有本质区别):

              document.body.appendChild(document.createElement('script')).src = 'source.js';
              

              【讨论】:

                【解决方案13】:

                以正确的顺序加载相互依赖的脚本。

                基于Satyam Pathak 响应,但修复了 onload。 它是在脚本实际加载之前触发的。

                const scripts = ['https://www.gstatic.com/firebasejs/6.2.0/firebase-storage.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-firestore.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-app.js']
                let count = 0
                
                  
                 const recursivelyAddScript = (script, cb) => {
                  const el = document.createElement('script')
                  el.src = script
                  if(count < scripts.length) {
                    count ++
                    el.onload = () => recursivelyAddScript(scripts[count])
                    document.body.appendChild(el)
                  } else {
                    console.log('All script loaded')
                    return
                  }
                }
                 
                  recursivelyAddScript(scripts[count])

                【讨论】:

                  【解决方案14】:

                  没有人提到它,但您也可以使用URLBlob 将实际源代码制作成一个URL,将其粘贴到脚本标签中:

                  const jsCode = `
                   // JS code in here. Maybe you extracted it from some HTML string.
                  `
                  
                  const url = URL.createObjectURL(new Blob([jsCode]))
                  const script = document.createElement('script')
                  script.src = url
                  URL.revokeObjectURL(url) // dispose of it when done
                  

                  至于jsCode,你可能是从一些HTML 中得到的。

                  下面是一个更完整的示例,说明如何处理 HTML 源代码中的任意数量的脚本:

                  main()
                  
                  async function main() {
                      const scriptTagOpen = /<script\b[^>]*>/g
                      const scriptTagClose = /<\/script\b[^>]*>/g
                      const scriptTagRegex = /<script\b[^>]*>[\s\S]*?<\/script\b[^>]*>/g
                  
                      const response = await fetch('path/to/some.html')
                      const html = await response.text()
                  
                      someElement.innerHTML = html
                  
                      // We need to get the script tags and manually add them to DOM
                      // because otherwise innerHTML will not execute them.
                      const codes =
                          html
                              .match(scriptTagRegex)
                              ?.map(code => code.replace(scriptTagOpen, '').replace(scriptTagClose, ''))
                              .map(code => URL.createObjectURL(new Blob([code]))) || []
                  
                      for (const code of codes) {
                          const script = document.createElement('script')
                          script.src = code
                          someElement.append(script)
                          URL.revokeObjectURL(code)
                      }
                  }
                  

                  【讨论】:

                    【解决方案15】:

                    window.addEventListener("load", init);
                    
                            const loadScript = async (url) => {
                                const response = await fetch(url);
                                const script = await response.text();
                                eval(script);
                            }
                    
                            function init() {
                                const wistiaVideo = document.querySelector(".wistia_embed");
                    
                                if ("IntersectionObserver" in window && "IntersectionObserverEntry" in window && "intersectionRatio" in window.IntersectionObserverEntry.prototype) {
                                    let lazyVideoObserver = new IntersectionObserver(function (entries, observer) {
                                        entries.forEach(function (entry) {
                                            if (entry.isIntersecting) {
                                                setTimeout(() => loadScript("//fast.wistia.com/assets/external/E-v1.js"), 1000);
                                                lazyVideoObserver.unobserve(entry.target);
                                                console.log("E-v1.js script loaded from fast.wistia.com");
                                            }
                                        });
                                    });
                                    lazyVideoObserver.observe(wistiaVideo);
                                }
                            }
                    <div style="height: 150vh; background-color: #f7f7f7;"></div>
                        <h1>Wistia Video!</h1>
                    
                        <div class="wistia_embed wistia_async_29b0fbf547" style="width:640px;height:360px;">&nbsp;</div>
                    
                    
                        <h1>Video Ended!</h1>

                    【讨论】:

                    • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
                    【解决方案16】:

                    差不多十年过去了,没有人费心写Promise版本,所以这是我的(基于this awnser):

                    function addScript(src) {
                      return new Promise((resolve, reject) => {
                        const s = document.createElement('script');
                    
                        s.setAttribute('src', src);
                        s.addEventListener('load', resolve);
                        s.addEventListener('error', reject);
                    
                        document.body.appendChild(s);
                      });
                    }
                    

                    用法

                    try {
                      await addScript('https://api.stackexchange.com/js/2.0/all.js');
                      // do something after it was loaded
                    } catch (e) {
                      console.log(e);
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2014-02-17
                      • 2021-06-13
                      • 2015-08-21
                      • 2014-03-10
                      • 2015-01-26
                      • 2022-11-23
                      相关资源
                      最近更新 更多