202

我想在网页中动态包含一个脚本标签,但是我无法控制它的 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>

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

我尝试过的jsfiddle

4

16 回答 16

263
var my_awesome_script = document.createElement('script');

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

document.head.appendChild(my_awesome_script);
于 2012-10-29T12:51:29.383 回答
115

您可以document.createElement()像这样使用该功能:

function addScript( src ) {
  var s = document.createElement( 'script' );
  s.setAttribute( 'src', src );
  document.body.appendChild( s );
}
于 2012-10-29T12:51:30.700 回答
86

有一个onload函数,可以在脚本成功加载时调用:

function addScript( src, callback ) {
  var s = document.createElement( 'script' );
  s.setAttribute( 'src', src );
  s.onload=callback;
  document.body.appendChild( s );
}
于 2014-12-14T10:41:04.737 回答
20

我写的一个很好的小脚本来加载多个脚本:

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
});
于 2016-06-19T09:04:45.960 回答
9

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

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

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

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

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

var scriptElm = document.createElement('script');
scriptElm.setAttribute('class', 'class-name');
var inlineCode = document.createTextNode('alert("hello world")');
scriptElm.appendChild(inlineCode); 
document.body.appendChild(scriptElm);
于 2019-12-26T12:19:12.383 回答
8

您可以尝试以下代码片段。

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(){});
于 2017-11-02T06:03:35.237 回答
7

单线(尽管与上述答案没有本质区别):

document.body.appendChild(document.createElement('script')).src = 'source.js';
于 2020-04-30T16:00:36.087 回答
5

这是为我工作。

你可以检查一下。

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

于 2019-11-18T05:45:40.893 回答
4

差不多十年后,没有人费心编写Promise版本,所以这是我的(基于这个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);
}
于 2021-10-18T12:45:03.827 回答
3

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

基于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])

于 2020-05-11T09:39:47.853 回答
2

好吧,有多种方法可以包含动态 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);

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

以正确的方式插入动态 Javascript

于 2018-05-18T19:21:28.053 回答
1

URL没有人提到它,但您也可以使用and将实际源代码粘贴到脚本标签中,方法是使用它创建一个 URL Blob

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)
    }
}
于 2021-03-12T05:05:09.250 回答
1

这样做的唯一方法是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)
  }
})()
于 2017-06-15T18:29:05.287 回答
1

我通过递归附加每个脚本来尝试它

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

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

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

于 2019-07-23T10:30:31.157 回答
0

这是一个缩小的片段,与 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为您的脚本路径。

于 2019-10-27T12:07:52.740 回答
0

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>

于 2021-09-21T06:32:53.430 回答