我如何引用加载当前正在执行的脚本的脚本标记?
我怎样才能引用加载当前正在运行的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);
我想要做的是引用加载当前脚本的脚本元素,这样我就可以将新dynamic加载的脚本标记附加到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>
如何获取当前脚本元素:
1.使用document.currentScript
document.currentScript
将返回当前正在处理其脚本的<script>
元素。
<script> var me = document.currentScript; </script>
优点
- 简单而明确。 可靠。
- 不需要修改脚本标签
- 适用于asynchronous脚本(
defer
和async
) - 适用于dynamic插入的脚本
问题
- 在旧的浏览器和IE中不起作用。
2.按IDselect脚本
给脚本一个id属性可以让你轻松地使用document.getElementById()
从id中select它。
<script id="myscript"> var me = document.getElementById('myscript'); </script>
优点
- 简单而明确。 可靠。
- 几乎普遍支持
- 适用于asynchronous脚本(
defer
和async
) - 适用于dynamic插入的脚本
问题
- 需要将自定义属性添加到脚本标记
- 对于某些边缘情况,
id
属性可能会导致某些浏览器中脚本的怪异行为
3.使用data-*
属性select脚本
给脚本一个data-*
属性可以让你轻松地从内部select它。
<script data-name="myscript"> var me = document.querySelector('script[data-name="myscript"]'); </script>
这比以前的select有一些好处。
优点
- 简单而明确。
- 适用于asynchronous脚本(
defer
和async
) - 适用于dynamic插入的脚本
问题
- 需要将自定义属性添加到脚本标记
- HTML5和
querySelector()
在所有浏览器中都不兼容 - 支持比使用
id
属性更less的支持 - 将绕过具有
id
边缘情况下的<script>
。 - 如果其他元素在页面上具有相同的数据属性和值,可能会感到困惑。
4.通过srcselect脚本
您可以使用select器来按源select脚本,而不是使用数据属性:
<script src="//example.com/embed.js"></script>
在embed.js中:
var me = document.querySelector('script[src="//example.com/embed.js"]');
优点
- 可靠
- 适用于asynchronous脚本(
defer
和async
) - 适用于dynamic插入的脚本
- 没有自定义属性或ID需要
问题
- 不适用于本地脚本
- 会在不同的环境中造成问题,如开发和生产
- 静态和脆弱。 更改脚本文件的位置将需要修改脚本
- 支持比使用
id
属性更less的支持 - 如果您加载相同的脚本两次将导致问题
5.遍历所有脚本find你想要的
我们也可以遍历每个脚本元素并单独检查每个脚本元素以select一个我们想要的:
<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"; }
这inheritance了所采取的任何方法的好处和问题,但不依赖于querySelector()
因此可以在较旧的浏览器中工作。
6.获取最后执行的脚本
由于脚本是按顺序执行的,因此最后的脚本元素通常是当前正在运行的脚本:
<script> var scripts = document.getElementsByTagName( 'script' ); var me = scripts[ scripts.length - 1 ]; </script>
优点
- 简单。
- 几乎普遍支持
- 没有自定义属性或ID需要
问题
- 不适用于asynchronous脚本(
defer
和async
) - 不能dynamic插入脚本
由于脚本是按顺序执行的,因此当前执行的脚本标记始终是页面上的最后一个脚本标记。 所以,要获得脚本标签,你可以这样做:
var scripts = document.getElementsByTagName( 'script' ); var thisScriptTag = scripts[ scripts.length - 1 ];
可能最简单的做法是给你的scrip标签一个id
属性。
这里有一个polyfill,它利用document.CurrentScript
如果它存在并回退到通过ID查找脚本)。
<script id="uniqueScriptId"> (function () { var thisScript = document.CurrentScript || document.getElementByID('uniqueScriptId'); // your code referencing thisScript here ()); </script>
如果将其包含在每个脚本标记的顶部,我相信您将能够始终知道哪个脚本标记正在被触发,还可以在asynchronouscallback的上下文中引用脚本标记。
未经testing,所以如果您尝试,请留下他人的反馈意见。
仅当脚本没有“延迟”或“asynchronous”属性时,才会按顺序执行脚本。 知道脚本标记的可能的ID / SRC / TITLE属性之一也可以在这些情况下工作。 所以Greg和Justin的build议都是正确的。
已经有WHATWG列表中的document.currentScript
的提议。
编辑 :火狐> 4已经实现了这个非常有用的财产,但它不是在IE11上次我检查,只在Chrome 29和Safari 8中可用。
编辑 :没有人提到“document.scripts”集合,但我相信以下可能是一个很好的跨浏览器替代获取当前正在运行的脚本:
var me = document.script[document.script.length -1];
警告一句话:修改仍然加载的DOM会导致你在IE6和IE7的世界受到伤害 。
在页面加载之后,您将会更好地运行该代码。
它必须工作在页面加载和脚本标记添加与JavaScript(例如与AJAX)
<script id="currentScript"> var $this = document.getElementById("currentScript"); $this.setAttribute("id",""); //... </script>
处理asynchronous和延迟脚本的方法是利用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); });
如果你可以假设脚本的文件名,你可以find它。 到目前为止,我只在Firefox中testing了以下function。
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');
请按照以下简单步骤获取对当前正在执行的脚本块的引用:
- 在脚本块中放置一些随机的唯一string(在每个脚本块中必须是唯一的/不同的)
- 迭代document.getElementsByTagName('script')的结果,从每个内容(从innerText / textContent属性获取)中查找唯一的string。
例子(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
考虑这个algorithm。 当您的脚本加载时(如果有多个相同的脚本),请查看document.scripts,find具有正确“src”属性的第一个脚本,并将其保存并用数据属性或唯一的className标记为“visited”。
加载下一个脚本时,再次扫描document.scripts,传递已标记为已访问的任何脚本。 采取该脚本的第一个未访问的实例。
这假设相同的脚本可能按照从头到身,从上到下,从同步到asynchronous的顺序执行。
(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; } })();
这将扫描所有脚本,查找未标记为特殊属性的第一个匹配脚本。
然后将该节点标记为数据属性,以便后续扫描不会select该节点。 这与graphics遍历BFS和DFSalgorithm类似,其中节点可能被标记为“已访问”以防止重新访问。
我已经得到了这个,这是在FF3,IE6和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 ); }