【问题标题】:Is `document.write` suitable for finding the element associated with the currently running script?`document.write` 是否适合查找与当前正在运行的脚本关联的元素?
【发布时间】:2013-01-19 03:14:58
【问题描述】:

如何找到与当前正在运行的脚本关联的脚本元素?我只对文档正文中的脚本感兴趣,而不是在头部(或其他地方)。

这是我目前所拥有的,它似乎工作正常。有没有我没有想到的潜在陷阱?

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

我为此考虑的用例涉及需要在文档中出现脚本标记的同一位置注入内容的脚本,因此延迟加载不会成为问题。换句话说,我只会在适当的地方调用这个函数,它不会作为 API 或任何东西的一部分公开。

我也知道problemdocument.write 和XHTML,在这种情况下我并不担心(到目前为止)。


我之前的尝试是这样的:

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

但是,正如我们在 this question 中看到的那样,如果将另一个脚本注入到文档中,这很容易失败。


我还在这个WHATWG spec 中找到了document.currentScript。在这一点上,它似乎没有得到广泛的支持。 document.currentScript 有一个有趣的 shim,它采用 another route... 脚本触发错误,捕获它,检查堆栈跟踪以找到脚本的 URL,然后找到具有匹配 src 属性的脚本元素.

这是一个聪明的解决方案,但它显然无法在 IE 中运行,并且如果多个脚本使用相同的 URL,它会中断。


理想情况下,我想找到一个与顶部代码示例给出相同结果的解决方案(没有额外要求),但不依赖于document.write。这可能吗?如果没有,假设正确使用此代码是否相当安全(仅在页面加载期间,而不是 XHTML)?


编辑:请参阅this answer 了解另一个潜在用例,以及为什么将br 元素用于占位符的详细信息。

【问题讨论】:

  • 如果代码在 DOM 加载后运行,document.write 将替换整个文档。
  • 加载文档后它没有运行...有关它如何运行的示例,请参阅链接的问题。仍在编辑这个问题。
  • 这是另一个相关问题,也许这就是您要找的:stackoverflow.com/questions/5261300/…
  • @JaredFarrish 查看此问题底部链接的问题的答案...在加载之前触发 JSONP 请求或类似请求的其他内容可能会搞砸。
  • 如果没有类似于 script.placeholder 标签上的类或其他内容,您似乎坚持使用 document.write

标签: javascript html dom


【解决方案1】:

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 的代码编写了一个

标记,但样式标记似乎更适用于 body 和 head 中的脚本......并且没有副作用。

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

更新:

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

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

参考资料:

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];

        }

    })()

【讨论】:

    【解决方案2】:

    如果您的脚本不是内联的(例如,它是通过“

    【讨论】:

    • 不完全可靠,因为来源可能是相对的。如果我写&lt;script src="i/dont/../know/../where/../../im/../going/../ohwait/thereitis.js"&gt;,那将严重影响您的解决方案;)
    【解决方案3】:

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

    var allScripts = document.getElementsByTagName("script"),
        currentScript = allScripts[allScripts.length-1];
    

    【讨论】:

    • 请重新阅读问题。 OP 明确排除了这一点,因为它可能无法正常工作。
    【解决方案4】:

    只是另一个想法:根据 w3schools 的说法,&lt;script&gt; tag supports global HTML attributes

    使用任何全局属性(idclasstitle)您可以区分使用的脚本标签,但在这种情况下您必须预先定义idclass

    【讨论】:

    • 虽然你的回答没有错,但这并不是这里的问题。
    • 在问题底部添加了指向另一个答案的链接,看看那个。现在,那里的代码看起来像:&lt;script&gt;transclude("foo")&lt;/script&gt;。例如,使用 ID 属性执行此操作,它看起来更像这样:&lt;script id="blat"&gt;transclude("blat", "foo")&lt;/script&gt; ... 不理想。
    • 实际上这也有效。 QuerySelector('#myscript') 让您可以访问 he 标签。似乎比您的其他选择更好
    • 我已经编辑了您的问题,只是为了向您展示如何创建一个更好看的答案。这可能会帮助您解决新问题(点击编辑按钮查看)。这似乎确实没有回答这个问题。代码的位置和执行代码的位置是两件不同的事情。看来这个问题是关于后者的(但我不会称自己为 JS 专家)。
    • 感谢您的帮助和 cmets。但是请停止点击向下链接来否定已经被击败的答案。
    猜你喜欢
    • 2020-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-11
    • 1970-01-01
    • 1970-01-01
    • 2021-09-22
    • 1970-01-01
    相关资源
    最近更新 更多