351

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

14 回答 14

764

如何获取当前脚本元素:

1.使用document.currentScript

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

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

好处

  • 简单明了。可靠的。
  • 不需要修改脚本标签
  • 适用于异步脚本 ( defer& async)
  • 适用于动态插入的脚本

问题

  • 不适用于旧版浏览器和 IE。
  • 不适用于模块<script type="module">

2.通过id选择脚本

给脚本一个 id 属性将使您可以轻松地从 using 中通过 id 选择它document.getElementById()

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

好处

  • 简单明了。可靠的。
  • 几乎普遍支持
  • 适用于异步脚本 ( defer& async)
  • 适用于动态插入的脚本

问题

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

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

给脚本一个data-*属性将使您可以轻松地从内部选择它。

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

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

好处

  • 简单明了。
  • 适用于异步脚本 ( defer& async)
  • 适用于动态插入的脚本

问题

  • 需要向脚本标签添加自定义属性
  • 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"]');

好处

  • 可靠的
  • 适用于异步脚本 ( defer& async)
  • 适用于动态插入的脚本
  • 无需自定义属性或 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

问题

  • 不适用于异步脚本 ( defer& async)
  • 不适用于动态插入的脚本
于 2014-03-30T15:02:49.060 回答
90

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

var scripts = document.getElementsByTagName( 'script' );
var thisScriptTag = scripts[ scripts.length - 1 ];
于 2010-07-24T19:29:04.380 回答
14

可能最简单的事情就是给你的脚本标签一个id属性。

于 2008-12-31T20:29:42.110 回答
12

仅当脚本没有“defer”或“async”属性时才会按顺序执行。在这些情况下,了解脚本标记的可能 ID/SRC/TITLE 属性之一也可以工作。所以 Greg 和 Justin 的建议都是正确的。

document.currentScriptWHATWG 列表中已经有一个提案。

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

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

var me = document.scripts[document.scripts.length -1];
于 2010-09-12T16:56:02.407 回答
12

这是一个 polyfill,document.CurrentScript如果它存在,它会利用它并回退到通过 ID 查找脚本。

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

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

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

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

于 2013-09-13T15:29:55.577 回答
8

它必须在页面加载和使用 javascript 添加脚本标记时工作(例如使用 ajax)

<script id="currentScript">
var $this = document.getElementById("currentScript");
$this.setAttribute("id","");
//...
</script>
于 2011-10-18T00:59:01.503 回答
5

要获取脚本,您可以使用当前加载的脚本

var thisScript = document.currentScript;

您需要在脚本的开头保留引用,以便稍后调用

var url = thisScript.src
于 2018-02-25T02:24:41.887 回答
4

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

  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
于 2012-02-26T07:44:44.427 回答
4

处理异步和延迟脚本的一种方法是利用 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);
});
于 2013-08-06T21:32:13.023 回答
3

考虑这个算法。当您的脚本加载时(如果有多个相同的脚本),请查看 document.scripts,找到具有正确“src”属性的第一个脚本,并将其保存并使用数据属性或唯一类名将其标记为“已访问”。

当下一个脚本加载时,再次扫描 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 算法,其中节点可能被标记为“已访问”以防止重新访问。

于 2017-06-12T18:43:55.030 回答
1

我有这个,它在 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 );
}
于 2008-12-31T21:12:16.627 回答
1

script我使用这种通常的替代方法动态插入标签,并在添加到 DOM 之前eval简单地设置一个全局属性。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从循环中连续多次调用。

于 2020-09-08T00:15:04.003 回答
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');
于 2009-11-11T02:05:20.970 回答
-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);
于 2018-08-20T18:22:17.597 回答