使用可能包含 document.write 的 src 动态添加脚本标签
Posted
技术标签:
【中文标题】使用可能包含 document.write 的 src 动态添加脚本标签【英文标题】:Dynamically add script tag with src that may include document.write 【发布时间】: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
【问题讨论】:
经过数小时苦苦挣扎的 postscribe 是解决方案! https://github.com/krux/postscribe/ async loading javascript with 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
);
【讨论】:
对不起...刚刚找到一个更好的允许依赖***.com/a/1867135/678780【参考方案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】:没有人提到它,但您也可以使用URL
和Blob
将实际源代码制作成一个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;"> </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);
【讨论】:
以上是关于使用可能包含 document.write 的 src 动态添加脚本标签的主要内容,如果未能解决你的问题,请参考以下文章
如何让使用 document.write() 创建的 JavaScript 执行?
动态调用包含document.write的js文件,动态调用外部js文件时,文件中alert起作用 document.write不起作用