如何引用加载当前正在运行的javascript的脚本元素?
这是情况。我在页面中加载了一个“主”脚本,首先是在HEAD标记下。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<script type="text/javascript" src="scripts.js"></script>
“scripts.js”中有一个脚本需要能够按需加载其他脚本。普通方法对我来说不太有用,因为我需要添加新脚本而不引用HEAD标记,因为HEAD元素还没有完成渲染:
document.getElementsByTagName('head')[0].appendChild(v);
我想要做的是引用加载当前脚本的脚本元素,然后我可以将新的动态加载的脚本标记添加到DOM之后。
<script type="text/javascript" src="scripts.js"></script>
loaded by scripts.js--><script type="text/javascript" src="new_script1.js"></script>
loaded by scripts.js --><script type="text/javascript" src="new_script2.js"></script>
答案 0 :(得分:568)
document.currentScript
document.currentScript
将返回其脚本当前正在处理的<script>
元素。
<script>
var me = document.currentScript;
</script>
defer
&amp; async
)为脚本提供一个id属性,您可以使用document.getElementById()
从内部轻松选择ID。
<script id="myscript">
var me = document.getElementById('myscript');
</script>
defer
&amp; async
)id
属性可能会导致某些浏览器中的脚本出现奇怪的行为data-*
属性为脚本提供data-*
属性可让您轻松地从内部选择它。
<script data-name="myscript">
var me = document.querySelector('script[data-name="myscript"]');
</script>
与以前的选项相比,这几乎没有什么好处。
defer
&amp; async
)querySelector()
在所有浏览器中都不合规id
属性<script>
边缘情况绕过id
。 您可以使用选择器按源选择脚本,而不是使用数据属性:
<script src="//example.com/embed.js"></script>
在embed.js中:
var me = document.querySelector('script[src="//example.com/embed.js"]');
defer
&amp; async
)id
属性我们还可以遍历每个脚本元素并单独检查每个脚本元素以选择我们想要的那个:
<script>
var me = null;
var scripts = document.getElementsByTagName("script")
for (var i = 0; i < scripts.length; ++i) {
if( isMe(scripts[i])){
me = scripts[i];
}
}
</script>
这使我们可以在不支持querySelector()
的旧版浏览器中使用以前的技术。例如:
function isMe(scriptElem){
return scriptElem.getAttribute('src') === "//example.com/embed.js";
}
这继承了所采取的任何方法的好处和问题,但不依赖于querySelector()
,因此可以在旧浏览器中使用。
由于脚本是按顺序执行的,因此最后一个脚本元素通常是当前运行的脚本:
<script>
var scripts = document.getElementsByTagName( 'script' );
var me = scripts[ scripts.length - 1 ];
</script>
defer
&amp; async
)答案 1 :(得分:87)
答案 2 :(得分:11)
最简单的做法可能是为您的脚本标记提供id
属性。
答案 3 :(得分:10)
只有在没有“defer”或“async”属性的情况下才会按顺序执行脚本。知道脚本标记的一个可能的ID / SRC / TITLE属性也可以在这些情况下工作。所以格雷格和贾斯汀的建议都是正确的。
WHATWG列表上已有document.currentScript
提案。
编辑:Firefox&gt; 4已经实现了这个非常有用的属性,但是IE11最后一次检查后无法使用它,只能在Chrome 29和Safari 8中使用。
编辑:没有人提到“document.scripts”集合,但我相信以下内容可能是一个很好的跨浏览器替代方案来获取当前正在运行的脚本:
var me = document.scripts[document.scripts.length -1];
答案 4 :(得分:9)
如果存在并使用document.CurrentScript
,则会使用<script id="uniqueScriptId">
(function () {
var thisScript = document.CurrentScript || document.getElementByID('uniqueScriptId');
// your code referencing thisScript here
());
</script>
填充这些填充符,并回退到通过ID查找脚本。
{{1}}
如果将此包含在每个脚本标记的顶部,我相信您将能够始终知道正在触发哪个脚本标记,并且您还能够在异步回调的上下文中引用脚本标记
未经测试,如果您尝试,请留下反馈给他人。
答案 5 :(得分:6)
它必须适用于页面加载以及使用javascript添加脚本标记时(例如使用ajax)
<script id="currentScript">
var $this = document.getElementById("currentScript");
$this.setAttribute("id","");
//...
</script>
答案 6 :(得分:3)
处理异步和处理的方法延迟脚本是利用onload处理程序 - 为所有脚本标记设置onload处理程序,第一个执行的脚本处理程序应该是你的。
function getCurrentScript(callback) {
if (document.currentScript) {
callback(document.currentScript);
return;
}
var scripts = document.scripts;
function onLoad() {
for (var i = 0; i < scripts.length; ++i) {
scripts[i].removeEventListener('load', onLoad, false);
}
callback(event.target);
}
for (var i = 0; i < scripts.length; ++i) {
scripts[i].addEventListener('load', onLoad, false);
}
}
getCurrentScript(function(currentScript) {
window.console.log(currentScript.src);
});
答案 7 :(得分:3)
要获取当前加载脚本的脚本,您可以使用
var thisScript = document.currentScript;
您需要在脚本的开头保留一个引用,以便稍后调用
var url = thisScript.src
答案 8 :(得分:2)
按照以下简单步骤获取对当前执行脚本块的引用:
示例(ABCDE345678是唯一ID):
<script type="text/javascript">
var A=document.getElementsByTagName('script'),i=count(A),thi$;
for(;i;thi$=A[--i])
if((thi$.innerText||thi$.textContent).indexOf('ABCDE345678'))break;
// Now thi$ is refer to current script block
</script>
顺便说一句,对于你的情况,你可以简单地使用旧式的document.write()方法来包含另一个脚本。
正如您所提到的那样,DOM尚未呈现,您可以利用浏览器始终以线性顺序执行脚本的事实(除了稍后将呈现的延迟脚本),因此文档的其余部分仍然“不存在”。
您通过document.write()编写的任何内容都将放在调用者脚本之后。
原始HTML页面的示例:
<!doctype html>
<html><head>
<script src="script.js"></script>
<script src="otherscript.js"></script>
<body>anything</body></html>
script.js的内容:
document.write('<script src="inserted.js"></script>');
渲染后,DOM结构将变为:
HEAD
SCRIPT script.js
SCRIPT inserted.js
SCRIPT otherscript.js
BODY
答案 9 :(得分:2)
考虑这个算法。加载脚本时(如果有多个相同的脚本),请查看document.scripts,找到带有正确&#34; src&#34;的第一个脚本。属性,并将其保存并标记为“已访问过”&#39;使用数据属性或唯一的className。
加载下一个脚本时,再次扫描document.scripts,传递任何已标记为已访问的脚本。获取该脚本的第一个未访问的实例。
这假设相同的脚本可能按照它们的加载顺序执行,从头到尾,从上到下,从同步到异步。
(function () {
var scripts = document.scripts;
// Scan for this data-* attribute
var dataAttr = 'data-your-attribute-here';
var i = 0;
var script;
while (i < scripts.length) {
script = scripts[i];
if (/your_script_here\.js/i.test(script.src)
&& !script.hasAttribute(dataAttr)) {
// A good match will break the loop before
// script is set to null.
break;
}
// If we exit the loop through a while condition failure,
// a check for null will reveal there are no matches.
script = null;
++i;
}
/**
* This specific your_script_here.js script tag.
* @type {Element|Node}
*/
var yourScriptVariable = null;
// Mark the script an pass it on.
if (script) {
script.setAttribute(dataAttr, '');
yourScriptVariable = script;
}
})();
这将扫描第一个匹配脚本的所有脚本,该脚本未标记特殊属性。
然后使用数据属性标记该节点(如果找到),以便后续扫描不会选择它。这类似于图遍历BFS和DFS算法,其中节点可以被标记为“访问过”&#39;防止再次访问。
答案 10 :(得分:0)
如果您可以采用脚本的文件名,则可以找到它。到目前为止,我只在Firefox中测试了以下功能。
function findMe(tag, attr, file) {
var tags = document.getElementsByTagName(tag);
var r = new RegExp(file + '$');
for (var i = 0;i < tags.length;i++) {
if (r.exec(tags[i][attr])) {
return tags[i][attr];
}
}
};
var element = findMe('script', 'src', 'scripts.js');
答案 11 :(得分:0)
我使用script
的通常替代方法动态插入eval
标签,并在添加到DOM之前直接设置了全局属性currentComponentScript
。
const old = el.querySelector("script")[0];
const replacement = document.createElement("script");
replacement.setAttribute("type", "module");
replacement.appendChild(document.createTextNode(old.innerHTML));
window.currentComponentScript = replacement;
old.replaceWith(replacement);
虽然不能循环工作。 DOM在下一个宏任务之前不会运行脚本,因此一批脚本将仅看到最后一个值集。您必须先setTimeout
整段,然后setTimeout
上一段结束后的下一段。即链接setTimeouts,而不仅仅是从循环中连续多次调用setTimeout
。
答案 12 :(得分:-1)
我有这个,这在FF3,IE6&amp; 7.在页面加载完成之前,按需加载脚本中的方法不可用,但这仍然非常有用。
//handle on-demand loading of javascripts
makescript = function(url){
var v = document.createElement('script');
v.src=url;
v.type='text/javascript';
//insertAfter. Get last <script> tag in DOM
d=document.getElementsByTagName('script')[(document.getElementsByTagName('script').length-1)];
d.parentNode.insertBefore( v, d.nextSibling );
}
答案 13 :(得分:-1)
我发现以下代码是最一致,最有效和最简单的代码。
var scripts = document.getElementsByTagName('script');
var thisScript = null;
var i = scripts.length;
while (i--) {
if (scripts[i].src && (scripts[i].src.indexOf('yourscript.js') !== -1)) {
thisScript = scripts[i];
break;
}
}
console.log(thisScript);