mer*_*erv 20 html javascript browser aggregation
是否可以在<head>客户端的HTML文档中删除脚本标记,然后再执行这些标记?
在服务器端,我能够在除了一个之外的<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);
Run Code Online (Sandbox Code Playgroud)
但是,记录的脚本数量只有1,因为(正如@ryan所指出的)代码是在DOM准备好之前执行的.虽然在document.ready事件回调中包装上面的代码确实能够正确计算其中的<script>标记数量<head>,但等待DOM准备就绪无法阻止脚本加载.
在DOM准备好之前是否有可靠的操作HTML的方法?
如果您需要更多上下文,这是尝试合并脚本的一部分,其中没有可用于服务器端聚合的选项.许多正在加载的JS库来自具有有限配置选项的CMS.内容大多是静态的,因此很少关注手动聚合JavaScript并从不同位置提供JavaScript.对于替代适用的聚合技术的任何建议也将受到欢迎.
由于您无法阻止将来的<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(); }
Run Code Online (Sandbox Code Playgroud)
在此示例中,代码阻止程序仅运行一次.您可以删除恢复行,或在1337调用后添加var counter=0;和if(++counter > 1337)恢复方法.
arguments.callee.caller是null如果呼叫者不是函数(例如,顶级代码).不是灾难,您可以从参数或this关键字或任何其他环境变量中读取,以确定是否必须停止执行.
演示:http://jsfiddle.net/qFnMX/
这是打破setter的一般模式:
Object.defineProperty(window, 'undefinable', {set:function(){}});
/*fail*/ function undefinable() {} // or window.undefinable = function(){};
Run Code Online (Sandbox Code Playgroud)
演示: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;}
Run Code Online (Sandbox Code Playgroud)
演示:http://jsfiddle.net/qFnMX/4/
等等.查看要阻止的脚本的源代码,您应该能够创建特定于脚本(甚至是通用)的脚本中断模式.
错误触发方法的唯一缺点是错误记录在控制台中.对于普通用户来说,这应该不是问题.
对,有一个比我的第一个稍微不那么疯狂的想法,但它确实取决于你能够在页面的头部插入标签的控制权:
简而言之,如果您可以在头部的<noscript>任何<script>声明之前插入我之前的标签,然后您可以将</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>
Run Code Online (Sandbox Code Playgroud)
您可以尝试使用DOM Mutation事件:
DOMAttrModified
DOMAttributeNameChanged
DOMCharacterDataModified
DOMElementNameChanged
DOMNodeInserted
DOMNodeInsertedIntoDocument
DOMNodeRemoved
DOMNodeRemovedFromDocument
DOMSubtreeModified
Run Code Online (Sandbox Code Playgroud)
像这样:
document.head.addEventListener ('DOMNodeInserted', function(ev) {
if (ev.target.tagName == 'SCRIPT') {
ev.target.parentNode.removeChild(ev.target);
}
}, false);
Run Code Online (Sandbox Code Playgroud)
您也可以尝试通过MutationObserver执行此操作的新方法
好的,所以我还没有在Internet Explorer中测试任何这个(我怀疑它会工作),并且不要因为黑客的可怕而责备我......我知道;)但它似乎在FireFox中工作, Mac OSX上的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){}
})();
Run Code Online (Sandbox Code Playgroud)
只是说我已经用几乎我能想到的每种类型的脚本标签测试了它,放在我可以放置的地方.我还没有一个人能够突破.正如我所说,有趣的问题......虽然我不知道上述在生产环境中的运作情况如何:S;)
基本上,这必须作为脚本标签放在head标签的顶部.
一个测试例子:
http://pebbl.co.uk/stackoverflow/12748067.html
| 归档时间: |
|
| 查看次数: |
7852 次 |
| 最近记录: |