11

How can I find the script element associated with the currently running script? I'm only interested in scripts inside the body of the document, not in the head (or elsewhere).

Here is what I have so far, it seems to work alright. Are there any possible pitfalls I'm not thinking of?

function getCurrentScriptElement() {
    var placeholderId = 'placeholder-' + Math.random(), 
        script, placeholder;
    document.write('<br id="' + placeholderId + '">');
    placeholder = document.getElementById(placeholderId);
    script = placeholder.previousSibling;
    placeholder.parentNode.removeChild(placeholder);
    return script;
}

The use case I have in mind for this involves scripts that need to inject content at the same place in the document where the script tag occurs, so delayed loading will not be an issue. In other words I will only be calling this function in places where it's appropriate, it's not going to be exposed as part of an API or anything.

I'm also aware of the problem with document.write and XHTML, and am not worried about it in this case (so far).


My previous attempt looked something like this:

function getCurrentScriptElement() {
    var scripts = document.getElementsByTagName('script');
    return scripts[scripts.length - 1];
}

But, as we see in this question, this could easily fail if another script has been injected into the document.


I also found document.currentScript in this WHATWG spec. It doesn't seem to be widely supported at this point. There's an interesting shim for document.currentScript that takes another route... The script triggers an error, catches it, inspects the stack trace to find the URL of the script, and then finds the script element with the matching src attribute.

It's a clever solution, but it apparently won't work in IE, and it would break if several scripts use identical URLs.


Ideally, I would like to find a solution that gives the same result as the top code sample (no extra requirements), but doesn't rely on document.write. Is this possible? If not, is this code fairly safe assuming it's used correctly (only during page load, not XHTML)?


Edit: See this answer for another potential use case, and details on why a br element is used for the placeholder.

4

4 回答 4

2

OP 对棘手的问题有很好的解决方案。然而,这里有一些小的建议来改进它。

该函数应在可用时使用 document.currentScript。无论情况如何,它都能很好地工作。在 FireFox +4、Chrome +29 和 Opera +16 中可用。

在 IE 6-10 中,script.readyState 可用于模拟 document.currentScript。无论情况如何,它似乎也能很好地工作。

为避免出现问题,该函数应在 document.readyState == "complete" 时退出,甚至在早期的“交互”阶段退出,具体取决于代码。document.currentScript 在“完成”时似乎停止工作。但是,document.write 停止在“交互式”,即查找脚本的标记写入方法将因延迟加载脚本而失败。

在使用 document.write() 之前,应检查脚本异步属性。标记为 async 的脚本与文档分离,并且 document.write() 方法将无法按预期工作。

OP 的代码编写了一个 <p> 标记,但样式标记似乎更适用于正文和头部中的脚本......并且没有副作用。

随机数标签 ID 似乎没有多大用处,因为该元素已被删除。使用可重用的 GUID 常量可能会更好。

更新:

我在下面更新了我的代码示例,以说明一种无需编写标签即可执行此操作的方法。它似乎可以在 Chrome、FireFox、Opera 和 IE 5-10 上正常工作。无论注入的脚本和脚本属性(延迟和异步)如何,都返回正确的值。难点是IE+11怎么办?微软在 IE11 中删除了 script.readyState,但没有提供替代方案。一种解决方法是使用“MutationObserver”来查找脚本。在 IE 中,这些事件事件每次在脚本执行之前发生(与之后发生的 onload 不同)。这很好用,除非有外部加载的脚本设置了 defer 或 async 属性。异步脚本特别困难,因为它们可以随时执行,而且并不总是以相同的顺序执行(我的观察)。

无论如何,我想我会发布这段代码,希望它可以提供一个起点并帮助其他人。

参考:

https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver

http://msdn.microsoft.com/en-us/library/ie/dn265034(v=vs.85).aspx

代码片段:

    var currentScript = (function() {

        // PRIVATE
        var observer, current, nodes;

        // IE+11 - untested with other browsers.
        if (!document.currentScript && typeof MutationObserver=='function') {
            observer = new MutationObserver(function(mutations) {
                if (document.readyState=='complete') observer.disconnect();
                mutations.forEach(function(mutation) {
                    nodes = mutation.addedNodes;
                    if (nodes.length && nodes[0].nodeName=='SCRIPT') {
                        current = nodes[0];
                    }
                });
            });
            observer.observe(window.document, {childList: true, subtree: true});
        }

        // PUBLIC
        return function() {

            if (document.readyState=='complete') return;

            // CHROME+29, FIREFOX+4, OPERA+16
            if (document.currentScript) return document.currentScript;

            var i, s=document.getElementsByTagName('SCRIPT');

            // MSIE+5-10
            if (s[0].readyState) 
                for(i=0; i<s.length; i++)
                    if (s[i].readyState=='interactive') return s[i];

            // IE+11
            if (current) return current;

            // BEST GUESS
            return s[s.length-1];

        }

    })()
于 2014-04-11T01:35:14.110 回答
0

如果你的脚本不是内联的(例如它是通过'<script src>'加载的,在try块中抛出一个错误然后在catch块中检查它的堆栈跟踪。你将能够确定文件名。你可以然后使用 'querySelector('[src="'+filename+'"]')' 找到标签并在它之前插入您的内容或它的兄弟。

于 2014-03-02T14:49:24.527 回答
-1

假设你的脚本没有被推迟,你可以这样做:

var allScripts = document.getElementsByTagName("script"),
    currentScript = allScripts[allScripts.length-1];
于 2014-03-02T14:51:47.363 回答
-2

另一个想法:根据 w3schools,<script>标签支持全局 HTML 属性

使用任何全局属性 ( id, class, title) 您可以区分使用的脚本标签,尽管在这种情况下您必须预先定义idand class

于 2013-04-11T12:23:53.820 回答