是否可以删除HTML文档客户端的<head>
中的脚本标记,然后才能执行这些标记?
在服务器端,我可以在<script>
中的所有其他<script>
代码上方插入<head>
,除了一个,我希望能够删除所有后续内容脚本。我无法从服务器端删除 <script>
标记。
(function (c,h) {
var i, s = h.getElementsByTagName('script');
c.log("Num scripts: " + s.length);
i = s.length - 1;
while(i > 1) {
h.removeChild(s[i]);
i -= 1;
}
})(console, document.head);
但是,记录的脚本数量仅为1,因为(正如@ryan指出的那样)代码在DOM准备好之前执行。虽然将上面的代码包装在document.ready
事件回调中,但确实能够正确计算<script>
中<head>
个标记的数量,等待DOM准备就绪,无法阻止脚本加载。
在DOM准备好之前是否有可靠的方法来操作HTML?
如果您需要更多上下文,这是尝试整合没有服务器端聚合选项的脚本的一部分。许多正在加载的JS库来自具有有限配置选项的CMS。内容大多是静态的,因此很少关注手动聚合JavaScript并从不同位置提供JavaScript。对于替代适用的聚合技术的任何建议也是受欢迎的。
答案 0 :(得分:9)
由于您无法阻止将来的<script>
代码进行评估(每当找到</script>
代码时,都会获取并评估<script>
的相应代码。<script src>
将阻止除非设置了async
属性,否则从进一步加载到获取源的文档,需要采取不同的方法。
在我提出解决方案之前,我问:什么可以阻止<script>
标记内的脚本执行?事实上,
<script>
。1是显而易见的,2可以从文档中获得,因此我将重点关注3.以下示例是显而易见的,需要针对实际用例进行调整。
以下是代理现有方法的一般模式:
(function(Math) {
var original_method = Math.random;
Math.random = function() {
// use arguments.callee to read source code of caller function
if (/somepattern/.test(arguments.callee.caller)) {
Math.random = original_method; // Restore (run once)
throw 'Prevented execution!';
}
return random.apply(this, arguments); // Generic method proxy
};
})(Math);
// Demo:
function ok() { return Math.random(); }
function notok() { var somepattern; return Math.random(); }
在此示例中,代码阻止程序仅运行一次。您可以删除恢复行,或添加var counter=0;
和if(++counter > 1337)
以在1337呼叫后恢复该方法。
arguments.callee.caller
为null
。不是灾难,您可以从参数或this
关键字或任何其他环境变量中读取,以确定是否必须停止执行。
演示:http://jsfiddle.net/qFnMX/
这是打破setter的一般模式:
Object.defineProperty(window, 'undefinable', {set:function(){}});
/*fail*/ function undefinable() {} // or window.undefinable = function(){};
演示:http://jsfiddle.net/qFnMX/2/
当然是吸气鬼:
(function() {
var actualValue;
Object.defineProperty(window, 'unreadable', {
set: function(value) {
// Allow all setters for example
actualValue = value;
},
get: function() {
if (/somepattern/.test(arguments.callee.caller)) {
// Restore, by deleting the property, then assigning value:
delete window.unreadable;
window.unreadable = actualValue;
throw 'Prevented execution!';
}
return actualValue;
},
configurable: true // Allow re-definition of property descriptor
});
})();
function notok() {var somepattern = window.unreadable; }
// Now OK, because
function nowok() {var somepattern = window.unreadable; }
function ok() {return unreadable;}
演示:http://jsfiddle.net/qFnMX/4/
等等。查看要阻止的脚本的源代码,您应该能够创建特定于脚本(甚至通用)的脚本中断模式。
错误触发方法的唯一缺点是错误记录在控制台中。对于普通用户来说,这应该不是问题。
答案 1 :(得分:8)
是的,还有另一个稍微不那么疯狂的想法,但它确实取决于你能够在页面的头部插入标签的控件:
简单地说,如果您可以在头部中的任何<noscript>
声明之前插入我之前的<script>
标记,那么您可以在末尾添加</noscript>
标记头部以及最终的脚本片段 - 在将noscript标记写回页面之前,你应该可以使用noscript标记之间的标记做任何你想做的事。
这种方法的优点在于脚本禁用代理只会忽略和解析标记,但启用脚本的代理会将内容存储到但不会使用 ...究竟是什么需要的。
虽然这是设计用于头部,但它可以很容易地在身体中使用,虽然它必须是一个单独的实现。这是因为它必须使用平衡且完整的节点树,因为标签的性质(除非你能设法将整个标记包装在noscript中?!?)。
它不是完全证明的,因为脚本可以位于头部和身体标签之外 - 至少在它们被解析之前 - 但它似乎对我迄今为止测试的所有内容非常自信...... 和它不依赖于随机的ajax驱动的代码,这些代码会在浏览器更新的第一个迹象时中断;)
另外,我也喜欢noscript标签中脚本标签的想法......
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<noscript id="__disabled__">
<script src="jquery.js"></script>
<title>Another example</title>
<script>alert(1);</script>
<link rel="stylesheet" type="text/css" href="core.css" />
<style>body { background: #ddd; }</style>
</noscript>
<script>
(function(){
var noscript = document.getElementById('__disabled__');
if ( noscript ) {
document.write(
String(noscript.innerHTML)
/// IE entity encodes noscript content, so reverse
.replace(/>/gi,'>')
.replace(/</gi,'<')
/// simple disable script regexp
.replace(/<script[^>]*>/gi,'<'+'!--')
.replace(/<\/script>/gi,'//--'+'>')
);
}
})()
</script>
</head>
答案 2 :(得分:7)
您可以尝试使用DOM Mutation事件:
DOMAttrModified
DOMAttributeNameChanged
DOMCharacterDataModified
DOMElementNameChanged
DOMNodeInserted
DOMNodeInsertedIntoDocument
DOMNodeRemoved
DOMNodeRemovedFromDocument
DOMSubtreeModified
像这样:
document.head.addEventListener ('DOMNodeInserted', function(ev) {
if (ev.target.tagName == 'SCRIPT') {
ev.target.parentNode.removeChild(ev.target);
}
}, false);
您也可以尝试通过MutationObserver
执行此操作的新方法答案 3 :(得分:6)
好的,所以我还没有在Internet Explorer中测试任何这些(我怀疑它会起作用),并且不要因为黑客的可怕而责备我......我知道;但它似乎在Mac OSX上的FireFox,Safari,Chrome和Opera中起作用 - 至少是最近这些使用者的公开发布。当我访问Windows机器时,我会看看是否可以改进它...虽然我对IE没有太多希望。
(function(xhr,d,de){
d = document;
try{
de = ((de = d.getElementsByTagName('html')[0])
? de : ( d.documentElement ? d.documentElement : d.body ));
/// this forces firefox to reasses it's dom
d.write(' ');
/// make an ajax request to get the source of this page as a string
/// this could be improved, I've just chucked it in as an example
if (window.XMLHttpRequest) {
xhr = new window.XMLHttpRequest;
}else{
xhr = new ActiveXObject("MSXML2.XMLHTTP");
}
if ( xhr ) {
/// open non-async so the browser has to wait
xhr.open('GET', window.location, false);
xhr.onreadystatechange = function (e,o,ns){
/// when we've got the source of the page... then
if ((o = e.target) && (o.readyState == 4) && (o.status == 200)) {
/// remove the script tags
window.ns = ns = String(o.responseText)
.replace(/<script[^>]*>/gi,'<'+'!--')
.replace(/<\/script>/gi,'//--'+'>');
/// fix for firefox - this causes a complete
/// rewrite of the main docelm
if ( 'MozBoxSizing' in de.style ) {
de.innerHTML = ns;
}
/// fix for webkit, this seems to work, whereas
/// normal document.write() doesn't. Probably
/// because the window.location resets the document.
else {
window.location = 'javascript:document.write(window.ns);';
}
}
};
xhr.send({});
}
}
catch(ex){}
})();
只是说我已经用几乎所有我能想到的脚本标签来测试它,放在我可以放置的地方。我还没有一个人能够突破。正如我所说,有趣的问题......虽然我不知道上述在生产环境中的运作情况如何:S;)
基本上,这必须作为脚本标签放在head标签的顶部。
测试示例:
答案 4 :(得分:1)
不,你不能
我现在找不到官方文档,但是当我在High Performance Javascript上阅读Nicholas Zakas时,当渲染引擎找到Tag脚本时,它会停止HTML渲染(因此没有创建其他节点) ),下载脚本并执行它。然后它继续呈现HTML。这就是为什么当你在标签上执行“document.write()”时,结果会在标签之后添加,然后呈现页面的其余部分。
(我不知道我是否可以在这里插入一本书......)
因此,它不像渲染页面,然后删除节点并且脚本不会被执行,当浏览器找到标记时,在执行此代码之前,您无法执行任何操作。
我们的产品遇到了一个非常类似的问题,我们在DOM中添加了一个脚本标签,我们需要在新标签执行开始之前执行一些代码,经过一周的研究,我们必须找到另一个解决方案。< / p>
抱歉,但我希望你不要像我们那样浪费那么多时间。无论如何,我会继续寻找浏览器规范。