如何通过postMessage执行函数
How to execute a function through postMessage
我在iframe中有以下代码:
window.addEventListener('message', function(e){
if(e.data == 'test')
console.log(e);
}, false);
在父文档中:
$('#the_iframe').get(0).contentWindow.postMessage('test', 'http://localhost/');
因此,父文档向 iframe 发送"测试"消息并且它可以工作。
但是如何在父文档中定义一个函数,并以某种方式通过postMessage将此函数发送到iframe,这将在本地执行该函数?
该函数对文档进行一些更改,如下所示:
var func = function(){
$("#some_div").addClass('sss');
}
(#some_div
存在于 iframe 中,而不是父文档中)
没有什么可以阻止您将字符串化函数作为 postmessage 事件数据传递。实现是微不足道的,对于任何函数声明,如
function doSomething(){
alert("hello world!");
}
您可以encodeURI
其字符串解释:
console.log(encodeURI(doSomething.toString()));
//function%20doSomething()%20%7B%0A%20%20%20%20alert(%22hello%20world!%22);%0A%7D
然后它可以作为闭包的一部分执行 - 一些不太富有想象力的东西,比如
eval('('+decodeURI(strCallback)+')();');
有一个没有跨框架架构的小提琴概念证明 - 我会看看我是否可以把一个 postMessage 版本放在一起,但托管 w/jsfiddle 并不简单
更新
正如承诺的那样,一个完整的模型可以工作(下面的链接)。通过正确的event.origin
检查,这将足够难以理解,但我知道我们的安全团队永远不会让eval
像这样投入生产:)
鉴于该选项,我建议在两个页面上规范化功能,以便只需要传递参数消息(即传递参数而不是函数);但是,在某些情况下,这绝对是首选方法。
父代码:
document.domain = "fiddle.jshell.net";//sync the domains
window.addEventListener("message", receiveMessage, false);//set up the listener
function receiveMessage(e) {
try {
//attempt to deserialize function and execute as closure
eval('(' + decodeURI(e.data) + ')();');
} catch(e) {}
}
帧代码:
document.domain = "fiddle.jshell.net";//sync the domains
window.addEventListener("message", receiveMessage, false);//set up the listener
function receiveMessage(e) {
//"reply" with a serialized function
e.source.postMessage(serializeFunction(doSomething), "http://fiddle.jshell.net");
}
function serializeFunction(f) {
return encodeURI(f.toString());
}
function doSomething() {
alert("hello world!");
}
原型模型:父代码和 iframe 代码。
你真的不能。尽管 postMessage 的(草案)规范谈到了结构化对象,例如嵌套对象和数组,但 [...]JavaScript 值(字符串、数字、日期等)和 [...] 某些数据对象,如 File Blob、FileList 和 ArrayBuffer 对象,大多数浏览器只允许字符串(当然包括 JSON)。在MDN或dev.opera阅读更多内容。然而,我很确定不可能发送函数对象,至少不能作为保留其范围的闭包。
因此,如果您真的想从父窗口执行一些代码,您将以字符串化函数并将其eval()
iframe 结束。但是,我认为任何应用程序都没有理由允许评估任意代码(即使来自注册域);最好构建一个消息 API,它可以接收 (JSON-) 字符串命令并调用自己的方法。
我会尝试不同的方法。为什么?Bergi已经解释了为什么它不会按照你想要的方式工作。
您可以在父页面中定义(和重新定义函数):
<html>
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript">
var fun = function() { alert('I am a function!'); };
$(function() {
// use this function to change div background color
fun = function() {
// get desired div
var theDiv = $('#frame').contents().find('#some_div');
theDiv.css('background', '#ff0000');
};
// or override it, if you want to change div font color
fun = function() {
var theDiv = $('#frame').contents().find('#some_div');
theDiv.css('color', '#ff0000');
};
// in this example second (font color changing) function will be executed
});
</script>
</head>
<body>
<iframe id="frame" src="frame.htm"></iframe>
</body>
</html>
并从框架页中调用您的函数:
<html>
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
parent.fun();
});
</script>
</head>
<body>
<div id="some_div">I am a div (but inside frame)!</div>
</body>
</html>
这可能不方便,但它有效。
扩展以下问题,您可以串化函数,使用 postMessage
发送函数体,然后使用 eval
执行它。
本质上,您正在做的是编组函数,以便可以将其发送到 iframe,然后在另一端解组它。为此,请使用以下代码:
Iframe:
window.addEventListener("message", function (event) {
var data = JSON.parse(event.data);
var callback = window[data.callback];
var value = data.value;
if (data.type === "function")
value = eval(value);
var callback = window[data.callback];
if (typeof callback === "function")
callback(value);
}, false);
function callFunction(funct) {
funct();
}
父母:
var iframe = $("#the_iframe").get(0).contentWindow;
postMessage(function () {
$("#some_div").addClass("sss");
}, "callFunction");
function postMessage(value, callback) {
var type = typeof value;
if (type === "function")
value = String(value);
iframe.postMessage({
type: type,
value: value,
callback: callback
}, "http://localhost");
}
一旦你在iframe中获取函数体并eval
它,你就可以像普通函数一样使用它。您可以将其分配给变量、传递变量、对其使用 call
或apply
、bind
变量等。
但请注意,函数的作用域是动态的(即函数中的任何非局部变量都必须已经在 iframe 窗口中定义)。
在您的情况下,您在函数中使用的 jquery $
变量是非局部的。因此,iframe 必须已经加载了 jquery。它不会使用父窗口中的 jquery $
变量。
您可以随时将postMessage
发送回 iframe,让 iframe 处理消息。当然,您必须计算它不会在iframe中的下一个命令之前执行。
- 等待回调函数执行
- 暂停函数执行流程,直到ajax请求完成
- Bigcommerce-是否可以在函数执行后更改文本
- 如何对jquery中的未命名函数执行.call()
- 等待函数执行后再继续
- mootools类型的函数将文本作为函数执行
- 停止从另一个函数执行Javascript函数
- Jquery回调函数执行多次
- 如何使即兴 jquery 暂停当前函数执行
- JavaScript 双函数执行
- 来自函数 Javascript 的 NaN 返回值 ||函数执行顺序
- 如何在 JS 中调用 2 次或更多次时延迟函数执行
- 函数执行的次数超出预期
- JS中函数执行错误
- 为什么这个函数执行了两次
- 从子函数执行父函数;t更新父级'的属性
- 如何在jQuery函数执行's处于活动状态
- 使用javascript函数执行php文件
- jQuery函数执行顺序
- javascript中同步函数执行中的问题