无法在 IE 中将函数从一个窗口传递到另一个窗口
Can't pass a function from one window to another in IE
我有两个窗口,一个是从另一个打开的,所以,我在"子"窗口中有一个opener
属性。
父窗口在全局范围内有一些函数,必须使用函数作为第一个参数调用(它将用作回调(。
两个页面都是从同一域打开的,所以,我没有任何同源政策限制(我希望如此(......
在子窗口中,我有这样的代码
if(window.opener) {
window.opener.myFunction(function() { ... });
}
一切正常,直到我尝试在IE中运行它。在此浏览器中,myFunction
接收的参数始终为 Object
类型(用 typeof
检查(。myFunction
代码是这样的:
window.myFunction = function(cb) {
alert('Callback type is ' + (typeof cb));
if(typeof cb == 'function')
cb();
else
alert('Not a function!');
}
现场演示:http://elifantiev.ru/ie-opener-issue/first.html
问题是:
- 这是符合标准的行为吗?
- 此问题是否有一些解决方法?
即使typeof返回"object",该函数仍按预期工作。 调用cb()
将执行该函数。
使用 typeof
来确定参数是否为函数的替代方法是测试所有函数都应具有的调用属性:
if (cb && cb.call) { cb(); }
如果你要传递给需要函数的东西,它可以像这样包装:
function newCb() {
return cb.apply(object, arguments);
}
另请注意,当将函数从父级传递到子级时,typeof 也是对象。 将原始函数与作为对象的函数进行比较(往返后(返回 true。 如果由于某种原因需要引用原始函数(例如,取消订阅时(,这一点很重要。
即使你现在可以让它工作,我也不会指望两个浏览器窗口直接调用彼此函数的能力存在更长时间。 即使从方案中消除跨域问题,也存在太多的安全问题。
实现此目的并确保其适用于所有浏览器的唯一面向未来的方法是使用所有现代浏览器(包括 IE8 及更高版本(都支持的跨文档消息传递 API。
当我需要解决这个问题时,我能找到的最详细的例子是MDN上的这篇window.postMessage文章。
归根结底是一方呼吁发布消息,例如:
otherWindow.postMessage(message, targetOrigin);
然后是另一端的事件处理程序:
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
alert("I got it!'n" + event.data);
}
如果窗口来自同一域,IE6 和 IE7 可能允许您现在进行这些跨窗口调用,但 IE8 及更高版本可能会希望您使用此 API,我猜其他浏览器最终将完全恢复到这种更安全的通信机制。
鉴于这种情况,我强烈建议使用共享库来共享您的代码(使用类似 LAB.js 或 require.js甚至只是 Jquery 的 getScript(( 函数(,然后使用跨文档消息传递系统发送事件,而不是您今天尝试使用的回调函数。
更新
有一些 polyfill 可用于为旧版浏览器添加 postMessage 支持。 由于您不进行跨域调用,因此我将从Ben Alman的Jquery postMessage插件开始。 我没有使用过它,但我使用了Ben出色的Jquery BBQ插件。 如果支持,它应该回退到内置方法,并且仅在旧浏览器中的替代方法中填充。 它声称可以在IE7中工作...
如果Ben的插件不能解决问题,那么您可以尝试easyXDM,但是设置起来看起来有点复杂。
这似乎是设计使然。
这是一篇关于它的文章
https://bugzilla.mozilla.org/show_bug.cgi?id=475038
由于每个窗口可以具有不同的原型链,因此无法按预期传递函数。 尝试执行以下操作来确认:
var popup = window.open('second.html');
window.myFunction = function(cb) {
alert(cb instanceof Function); //false
alert(cb instanceof popup.Function); //true
}
因此,如果父端有许多函数需要担心,我将如何进行适当的函数验证(我讨厌原型,但这次我认为你卡住了(:
父窗口:
<html>
<script type="text/javascript">
/* give all our functions the validation check at once */
Function.prototype.remote = function(cb) {
if(cb instanceof popup.Function) {
this(cb);
} else {
alert('Not a function!');
}
};
var popup,
myFunction = function(cb) {
cb();
},
test = function(cb) {
cb();
}
</script>
<body>
<a href="#" onclick="popup = window.open('second.html'); return false;">Open window</a>
</body>
</html>
子窗口:
<html>
<body>
<script type="text/javascript">
var Parent = window.opener;
if(Parent) {
Parent.myFunction.remote(function(){
alert('callback!');
});
Parent.test.remote(function() {
alert('callback again');
});
}
</script>
This is a second page!
</body>
</html>
这是一个工作示例:http://dl.dropbox.com/u/169857/first.html
打开子窗口后,可以将父级创建的函数注册到其window
上下文中。您可以看到以下代码在 jsFiddle 中运行:
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
// will be called by the child window
function MyExportedCallback(x) {
$("#my-callback-result").text("this callback was run with '" + x + "'.");
}
// will open a child window and create content for testing
function MyLinkHandler() {
var wnd = window.open("about:blank");
// IMPORTANT: this is where the parent defined function is "registered"
// into child window scope
wnd["myExternal"] = MyExportedCallback;
// auxiliary code to generate some HTML to test the external function
var put = function(x) { wnd.document.write(x); };
put("<html><body>");
put("<a href='#' onclick='window.myExternal(123);'>");
put("click me to run external function");
put("</a>");
put("</body></html>");
}
// attach events
$.ready(function() {
$("#my-link").click(MyLinkHandler);
});
</script>
<body>
<p><a id="my-link" href="#">Open window</a></p>
<p id="my-callback-result">waitting callback...</p>
</body>
</html>
- 创建一个类似链接的按钮,并通过Javascript函数打开一个新的弹出窗口
- 用javascript将数据从一个窗口传递到另一个窗口
- 构建JS测试,警报窗口重复上一个Q,而不是问下一个Q
- 创建一个方法,通过一个窗口进行迭代并获取Titanium中的所有控件
- 当满足PHP条件时显示一个弹出窗口
- 按下一个HTML按钮,该按钮使用一个功能在同一个新窗口中打开URL
- 想要打开从链接到另一个页面的模式弹出窗口
- 为位于路线上的谷歌地图标记(起点和终点)设置一个信息窗口
- 如何打开一个新窗口或选项卡,并将其提供给javascript执行
- 在屏幕中间打开一个弹出窗口
- window.open:是否可以通过修改DOM来打开一个新窗口
- 有没有一种方法可以检测何时触发了溢出-y:auto,并因此创建一个弹出窗口
- 我需要帮助弄清楚一旦窗口的垂直高度被滚动,如何切换一个元素
- 获取上一个和当前窗口宽度
- 从另一个窗口访问document.getElementById
- 将jquery ui窗口的父元素设置为另一个元素
- 从另一个dojo模板窗口小部件调用dojo模板小部件中的函数
- notify.js没有'如果另一个窗口被聚焦,则不显示通知
- Bootbox,两个模式窗口-一个在另一个之上
- 如何给这个弹出窗口一个滚动条