使用 JavaScript 来防止后面的 `<script>` 标签被评估?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4726362/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Use JavaScript to prevent a later `<script>` tag from being evaluated?
提问by Trevor Burnham
This is a bit of an oddball use case, but I have my reasons:
这是一个有点奇怪的用例,但我有我的理由:
I'd like to be able to write
我希望能够写
<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
in my markup and, using the code in first.js, prevent or delay the execution of second.js. Is this possible, in any browser? What if the contents of first.jsare inlined? (If it helps, assume that the second script tag has an idattribute.)
在我的标记中,并使用 中的代码first.js,防止或延迟second.js. 这可能吗,在任何浏览器中?如果内容first.js是内联的怎么办?(如果有帮助,假设第二个脚本标签有一个id属性。)
Since I've gotten a couple of answers that missed what I'm getting at, I should clarify:
由于我得到的几个答案没有得到我的理解,我应该澄清一下:
- The solution must be entirely within
first.js. Anything that require changes to the original HTML of the page, or tosecond.js, is not acceptable. - It isacceptable to load
second.jsvia Ajax and execute it usingeval. That's the easy part. The hard part is preventing the immediate execution ofsecond.js. - Assume that you don't knowwhat's in
second.js. So, you can't just replace each global function called bysecond.jswith a no-op function. (Plus, this would almost certainly lead to errors.)
- 解决方案必须完全在
first.js. 任何需要更改页面原始 HTML 或 的内容second.js都是不可接受的。 - 它是可以接受的加载
second.js通过Ajax以及使用执行它eval。这是容易的部分。困难的部分是防止立即执行second.js. - 假设你不知道做什么是在
second.js。因此,您不能仅用second.js无操作函数替换由 调用的每个全局函数。(另外,这几乎肯定会导致错误。)
If you know of a solution that works in some browsers but not in others, I'd love to hear it.
如果您知道在某些浏览器中有效但在其他浏览器中无效的解决方案,我很想听听。
Example: To make this a little more concrete, let's say that the code
示例:为了更具体一点,假设代码
<script type="text/javascript">
function func() {
window.meaningOfLife = 42;
window.loadSecond();
};
setTimeout(func, 10);
</script>
precedes the two scriptincludes, and that second.jscontains the line
在两个script包含之前,并且second.js包含该行
if (window.meaningOfLife !== 42) {throw new Error();}
first.jsshould be able to prevent this error by delaying second.jsfrom executing until window.loadSecondis run. (Assume the implementation of window.loadSecondis also in first.js.) It is notallowed to touch window.meaningOfLife.
first.js应该能够通过延迟second.js执行直到window.loadSecond运行来防止此错误。(假设执行的window.loadSecond也是first.js。)它是不是不准碰window.meaningOfLife。
Update: Alohci's answer meets these requirements, but only on the condition that the second script tag comes immediatelyafter the first, with nothing but whitespace in between. If someone could extend his hack to avoid that requirement, without introducing other unwanted consequences, that would be amazing...
更新:Alohci 的答案满足这些要求,但前提是第二个脚本标签紧跟在第一个标签之后,中间只有空格。如果有人可以扩展他的 hack 以避免该要求,而不引入其他不良后果,那将是惊人的......
回答by Alohci
Given your specific requirements set, this is actually quite simple and should work completely cross-browser. It does require however, that first.js immediately precedes second.js without anything between them except white space.
考虑到您的特定要求集,这实际上非常简单,并且应该完全跨浏览器工作。然而,它确实要求 first.js 紧跟在 second.js 之前,它们之间除了空格之外没有任何东西。
First, let's assume that the HTML looks like this:
首先,让我们假设 HTML 如下所示:
<!DOCTYPE html>
<html>
<head>
<title>Test Case</title>
<meta charset="UTF-8" />
<script type="text/javascript">
function func() {
window.meaningOfLife = 42;
window.loadSecond();
};
</script>
<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
</head>
<body>
<p>Lorem ipsum dolor sit amet ...</p>
<a href="javascript:func()">Run Func()</a>
</body>
</html>
I've removed the setTimeout because that can cause func() to run before start.js runs causing a "loadSecond is not defined" error. Instead, I've provided an anchor to be clicked on to run func().
我已经删除了 setTimeout ,因为这会导致 func() 在 start.js 运行之前运行,从而导致“loadSecond is not defined”错误。相反,我提供了一个可单击以运行 func() 的锚点。
Second, let's assume that second.js looks like this:
其次,让我们假设 second.js 看起来像这样:
document.body.appendChild(document.createTextNode("second.js has run. "));
if (window.meaningOfLife !== 42) {throw new Error();}
Here, I've just added a line to append some text to the document body, so that it is easier to see when second.js actually runs.
在这里,我刚刚添加了一行以将一些文本附加到文档正文,以便更容易看到 second.js 何时实际运行。
Then the solution for first.js is this:
那么 first.js 的解决方案是这样的:
function loadSecond()
{
var runSecond = document.createElement("script");
runSecond.setAttribute("src", "second.js");
document.body.appendChild(runSecond);
}
document.write("<script type='application/x-suppress'>");
The loadSecond function is just there to run second.js when func() runs.
loadSecond 函数只是在 func() 运行时运行 second.js 。
The key to the solution is the document.writeline. It will inject into the HTML <script type='application/x-suppress'>between the close script tag of first.js and the open script tag of second.js.
解决方案的关键是document.write线路。它将注入到<script type='application/x-suppress'>first.js 的关闭脚本标记和 second.js 的打开脚本标记之间的 HTML。
The parser will see this and start a new script element. Because the type attribute has a value which is not one that the browser recognises as being JavaScript, it will not attempt to run its content. (So there are an infinite number of possible type attribute values you could use here, but you must include a type attribute, as in its absence, the browser will assume that the script's content is JavaScript.)
解析器将看到这一点并启动一个新的脚本元素。因为 type 属性的值不是浏览器识别为 JavaScript 的值,所以它不会尝试运行其内容。(因此,您可以在此处使用无数种可能的 type 属性值,但您必须包含一个 type 属性,因为如果没有该属性,浏览器将假定脚本的内容是 JavaScript。)
The second.js script's opening tag will then be parsed as text content of the new script element and not executed. Finally the second.js script's closing tag will be re-purposed to close the new script element instead, which means that the remainder of the HTML is parsed correctly.
second.js 脚本的开始标签随后将被解析为新脚本元素的文本内容并且不会被执行。最后, second.js 脚本的结束标记将重新用于关闭新的脚本元素,这意味着 HTML 的其余部分将被正确解析。
You can see a working version at http://www.alohci.net/static/jsprevent/jsprevent.htm
你可以在http://www.alohci.net/static/jsprevent/jsprevent.htm看到一个工作版本
回答by Kyle Wild
In first.js, set var shouldILoad = true
在first.js,设置var shouldILoad = true
Then, load second.jsthis way:
然后,以second.js这种方式加载:
<script>
if (shouldILoad) {
(function() {
var myscript = document.createElement('script');
myscript.type = 'text/javascript';
myscript.src = ('second.js');
var s = document.getElementById('myscript');
s.parentNode.insertBefore(myscript, s);
})();
}
</script>
(where 'myscript' is the ID of some element before which you'd like to insert the new Script element)
(其中 'myscript' 是您想在其前插入新 Script 元素的某个元素的 ID)
回答by Marcel Korpel
As far as I know, you can't. If the markup looks like
据我所知,你不能。如果标记看起来像
<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
you can't access the second script element from within first.js, as it hasn't been added to the DOM at the moment the first script runs (even not if you assign an idto the second element). It doesn't matter whether the code of second.jsis put inline or in an external file.
您无法从内访问第二个脚本元素first.js,因为在第一个脚本运行时它尚未添加到 DOM(即使您将 分配id给第二个元素也不会)。代码second.js是放在内联还是放在外部文件中都没有关系。
The only thing I don't understand is your second point. First you say that you can't control the markup of the document, but then you state it is possible to load second.jsdynamically (using AJAX).
我唯一不明白的是你的第二点。首先你说你不能控制文档的标记,但是你说可以second.js动态加载(使用 AJAX)。
回答by gblazex
All <script>tags have their own execution context, which makes it nearly impossible to interfere with each other. Of course you've got the (infamous) global object(referenced by windowin browsers).
所有<script>标签都有自己的执行上下文,这使得几乎不可能相互干扰。当然,您已经获得了(臭名昭著的)全局对象(window在浏览器中被引用)。
Preventing the execution of second.jsis rather simple: breakit!
Assuming that second.jstries to call document.getElementByIdfor example:
阻止执行second.js相当简单:打破它!假设second.js尝试调用document.getElementById例如:
Working exampleof breaking jQuery, then loading later (with dependecies).
Tested on: IE 6+, FF 3.6+, Chrome
打破 jQuery,然后加载(依赖)的工作示例。
测试:IE 6+、FF 3.6+、Chrome
end of first.js
first.js 结束
var execute;
// saving our position
var scripts = document.getElementsByTagName("script");
var i = scripts.length;
// breaking getElementById
var byId = document.getElementById;
document.getElementById = null;
var interval = setInterval(function () {
if (i != scripts.length) {
var second = scripts[i];
// stop polling
clearInterval(interval);
// fix getElementById
document.getElementById = byId;
// set the delayed callback
execute = function (onload) {
var script = document.createElement("script");
script.src = second.src;
script.onload = script.onreadystatechange = onload;
document.getElementsByTagName("head")[0].appendChild(script);
};
}
}, 100);
anytime you wanna execute second.js
任何时候你想执行 second.js
execute(function(){
// second.js dependant code goes here...
});
Note: the onloadparameter for executeis optional.
注意:onload参数 forexecute是可选的。
回答by santriseus
Following articledescribes the way you could block (3-rd party) scripts loading/execution from your script (including the both tag in the page head and dynamically added tags).
以下文章描述了您可以阻止(第 3 方)脚本从您的脚本加载/执行的方式(包括页面标题中的两个标签和动态添加的标签)。
To handle existing tags on a page:
要处理页面上的现有标签:
- Use a MutationObserver to observe script elements insertion and inside the MutationObserver callback backup the script (to enable/insert it later) and change the script type to "javascript/blocked" (not works in IE, Edge, Firefox). Also you could handle deprecated (but working) beforescriptexecuteevent in Firefox to prevent script load.
- Manually set type "javascript/blocked" (works everywhere including IE and Edge) like
<script type="text/javascript" type="javascript/blocked" src="second.js"></script>, then backup it in MutationObserver callback and re-add it later.
- 使用 MutationObserver 观察脚本元素插入并在 MutationObserver 回调中备份脚本(稍后启用/插入)并将脚本类型更改为“javascript/blocked”(不适用于 IE、Edge、Firefox)。您也可以在 Firefox 中处理已弃用(但有效)的beforescriptexecute事件以防止脚本加载。
- 手动设置类型“javascript/blocked”(适用于任何地方,包括 IE 和 Edge)
<script type="text/javascript" type="javascript/blocked" src="second.js"></script>,然后在 MutationObserver 回调中备份它,稍后重新添加。
To handle dynamically added tags
处理动态添加的标签
- Monkey-patch the document.createElement.
- Override ‘src' and ‘type' descriptors on the HTMLScriptElement prototype.
- 猴子修补document.createElement。
- 覆盖 HTMLScriptElement 原型上的“src”和“type”描述符。
Also this guys provide a yettlibrary with the approach described in the article.
此外,这些家伙提供了一个使用文章中描述的方法的Yett库。
回答by sdleihssirhc
Just to see if this was possible, I had first.jssend a synchronousXHR to a PHP file, and had the PHP file delete second.js. When the readyState reached '4', I had the JS alert something, to stop the thread. Then I went and checked the server... Yeah, second.jswas deleted. And yet, it wouldn't work. I'd close the alert box, and the code that was insecond.jswould still be executed, despite the fact that the file was gone.
只是为了看看这是否可能,我已将同步XHRfirst.js发送到 PHP 文件,并删除了 PHP 文件。当 readyState 达到“4”时,我让 JS 发出警报,以停止线程。然后我去检查了服务器......是的,被删除了。然而,这行不通。我会关闭警告框,这是该代码中仍然会执行,尽管该文件不见了。second.jssecond.jssecond.js
I don't really know what this means, but the answer to your question is probably, "No, it's not possible."
我真的不知道这意味着什么,但您问题的答案可能是,“不,这是不可能的。”
回答by Gerhard Presser
you may use setTimeout() to delay the execution of some code
您可以使用 setTimeout() 来延迟某些代码的执行

