如何引用加载了当前正在执行的脚本的脚本标签?


303

如何引用加载了当前正在运行的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);

我想做的是引用加载了当前脚本的script元素,以便随后可以将新的动态加载的脚本标签附加到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
一个警告:在DOM仍加载时修改DOM会对您造成IE6&IE7的痛苦。页面加载后最好运行该代码。
三联画

6
看起来现在就在caniuse上了:caniuse.com/#feat=document-currentscript
Tyler

Answers:


653

如何获取当前脚本元素:

1.使用 document.currentScript

document.currentScript将返回<script>当前正在处理其脚本的元素。

<script>
var me = document.currentScript;
</script>

好处

  • 简单明了。可靠。
  • 不需要修改脚本标签
  • 适用于异步脚本(deferasync
  • 与动态插入的脚本一起使用

问题

  • 在较旧的浏览器和IE中将无法使用。
  • 不适用于模块 <script type="module">

2.按编号选择脚本

为脚本提供id属性可以让您轻松地通过使用id通过id选择它document.getElementById()

<script id="myscript">
var me = document.getElementById('myscript');
</script>

好处

  • 简单明了。可靠。
  • 几乎得到普遍支持
  • 适用于异步脚本(deferasync
  • 与动态插入的脚本一起使用

问题

  • 需要向脚本标签添加自定义属性
  • id 属性在某些情况下可能会导致某些浏览器中脚本的怪异行为

3.使用data-*属性选择脚本

给脚本一个data-*属性可以让您轻松地从中选择它。

<script data-name="myscript">
var me = document.querySelector('script[data-name="myscript"]');
</script>

与以前的选项相比,这没有什么好处。

好处

  • 简单明了。
  • 适用于异步脚本(deferasync
  • 与动态插入的脚本一起使用

问题

  • 需要向脚本标签添加自定义属性
  • HTML5,querySelector()并非所有浏览器都兼容
  • 与使用id属性相比,受支持的范围较广
  • 会得到解决<script>id边缘情况。
  • 如果另一个元素在页面上具有相同的数据属性和值,则可能会感到困惑。

4.通过src选择脚本

您可以使用选择器按来源选择脚本,而不是使用数据属性:

<script src="//example.com/embed.js"></script>

在embed.js中:

var me = document.querySelector('script[src="//example.com/embed.js"]');

好处

  • 可靠
  • 适用于异步脚本(deferasync
  • 与动态插入的脚本一起使用
  • 无需自定义属性或ID

问题

  • 难道不是对本地脚本工作
  • 将在不同的环境中引起问题,例如开发和生产
  • 静态和脆弱。更改脚本文件的位置将需要修改脚本
  • 与使用id属性相比,受支持的范围较广
  • 如果您两次加载相同的脚本,将导致问题

5.遍历所有脚本以查找所需的脚本

我们还可以遍历每个脚本元素,并逐个检查每个脚本元素以选择所需的元素:

<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()因此在较旧的浏览器中也可以使用。

6.获取最后执行的脚本

由于脚本是按顺序执行的,因此最后一个脚本元素通常是当前正在运行的脚本:

<script>
var scripts = document.getElementsByTagName( 'script' );
var me = scripts[ scripts.length - 1 ];
</script>

好处

  • 简单。
  • 几乎得到普遍支持
  • 无需自定义属性或ID

问题

  • 难道不是异步脚本工作(deferasync
  • 难道不是与动态插入脚本工作

4
这应该是答案。
罗伊·纳米尔

1
同意@RoyiNamir。这是最好的答案。
汉斯(Hans)

27
谢谢大家,但您知道我在接受答案后4 就回答了,对:)
brice

5
“ document.currentScript”不适用于动态加载的脚本,在最新的chrome / firefox中返回null,“上次执行的脚本”有效
xwild

1
不工作的时候script是在template插入一个影子DOM
Supersharp

85

由于脚本是按顺序执行的,因此当前执行的脚本标记始终是页面上的最后一个脚本标记。因此,要获取脚本标签,您可以执行以下操作:

var scripts = document.getElementsByTagName( 'script' );
var thisScriptTag = scripts[ scripts.length - 1 ];

3
这是简单而优雅的。如果您解压缩javascript,则新的Google Charts / Visualizations API中有一个示例。他们从script标签内加载JSON数据,请参阅: ajax.googleapis.com/ajax/static/modules/gviz/1.0/chart.js
Jason Thrasher,2010年

1
这是个好主意,通常对我有用。但我应该补充一点,就是有时候我发现它返回对另一个脚本的引用。不知道为什么-一直无法找到原因。因此,我通常采用不同的方法,例如,我对脚本文件的名称进行了硬编码,然后使用该文件名查找脚本标签。
肯·史密斯,

53
我可以想到的一个实例可能是在将脚本标记异步添加到DOM时返回错误的结果。
Coffee Bite

9
是的,这可能会有不可预测的结果,因此您可以尝试使用选择器:$('script [src * =“ / mysource.js”]')???
杰森·塞布林

7
在页面加载后加载脚本时,该功能不起作用。您可能不会获得正确的标签。
ThemeZ

11

可能最简单的方法是为您的Scrip标记赋予一个id属性。


2
尽管您是对的,但在很多情况下,OP的问题仍然有效,其中有两种情况:1)进行爬网时2)使用客户端的DOM时,他不愿意更改
nichochar

10

仅当脚本没有“ defer”或“ async”属性时,才按顺序执行脚本。在这些情况下,了解脚本标签的可能的ID / SRC / TITLE属性之一也可以工作。因此,格雷格和贾斯汀的建议都是正确的。

document.currentScript在WHATWG清单上已经有一个建议。

编辑:Firefox> 4已经实现了这个非常有用的属性,但是我上次检查它在IE11中不可用,并且仅在Chrome 29和Safari 8中可用。

编辑:没人提到“ document.scripts”集合,但我相信以下可能是获取当前运行脚本的一个很好的跨浏览器替代方法:

var me = document.scripts[document.scripts.length -1];

1
它是document.scripts而非document.script
Moritz

9

这是一个polyfill,可以利用document.CurrentScript它的存在,但可以通过ID查找脚本。

<script id="uniqueScriptId">
    (function () {
        var thisScript = document.CurrentScript || document.getElementByID('uniqueScriptId');

        // your code referencing thisScript here
    ());
</script>

如果您将其包含在每个脚本标签的顶部,我相信您将能够一致地知道正在触发哪个脚本标签,并且还可以在异步回调的上下文中引用该脚本标签。

未经测试,因此请尝试给他人留下反馈。


但是,该id属性在元素中无效script。这种方法会产生什么样的问题?
NR

1
@nr-不,所有元素都可以具有id属性。idclassslot在DOM级别而不是HTML级别定义。如果转到HTML中全局属性并滚动到列表上方,则会发现“ DOM标准定义了任何名称空间中任何元素的类,id和插槽属性的用户代理要求。” 后跟“可以在所有HTML元素上指定class,id和slot属性”。DOM规范在这里进行了介绍
TJ Crowder

6

它必须在页面加载时以及使用javascript(例如,用ajax)添加脚本标签时起作用

<script id="currentScript">
var $this = document.getElementById("currentScript");
$this.setAttribute("id","");
//...
</script>

3

请按照以下简单步骤获取对当前正在执行的脚本块的引用:

  1. 在脚本块中放置一些随机的唯一字符串(在每个脚本块中必须唯一/不同)
  2. 迭代document.getElementsByTagName('script')的结果,从它们的每个内容中查找唯一的字符串(从innerText / textContent属性获得)。

示例(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

这似乎仅适用于内联脚本,不适用于外部脚本。在后一种情况下,所有属性innerText,text和textContent均为空。
Jos de Jong 2014年

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


2

考虑这个算法。加载脚本时(如果有多个相同的脚本),请浏览document.scripts,找到第一个具有正确“ src”属性的脚本,然后保存并使用数据属性或唯一的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;
  }
})();

这将在所有脚本中进行扫描,以查找第一个没有用special属性标记的匹配脚本。

然后,如果找到该节点,则将其标记为数据属性,以便后续扫描不会选择它。这类似于图遍历BFS和DFS算法,其中节点可能被标记为“已访问”以防止重新访问。


欢迎使用堆栈溢出。您是否愿意在算法中包含一些代码?
Gary99

塔尔,@ Gary99
LSOJ,

0

我已经知道了,它可以在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 );
}

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');

1
很落伍。可以使用一个简单的oneliner querySelector来完成!
Fabian von Ellerts

-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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.