2012-05-08 35 views
9

我有两个窗口,一个是从另一个窗口打开的,所以我在“子窗口”中有一个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

的问题是:

  1. 这是一个standarts合规行为?
  2. 是否有解决此问题的一些解决方法?
+0

请指定遇到此问题的IE版本。 –

+0

IE7 +(IE 7,8,9) – Olegas

回答

6

即使typeof返回“object”,该函数仍然按预期工作。调用cb()将执行该功能。

使用typeof,以确定是否该参数是一个功能适用于所有功能预计将有通话性能测试一种替代方案:

if (cb && cb.call) { cb(); } 

如果你是沿着传递的东西,需要一个功能,它可以像这样包装:

function newCb() { 
    return cb.apply(object, arguments); 
} 

还要注意,当从父类传递函数到子类型时,typeof也是对象。将原始函数与函数作为对象比较(往返之后)返回true。如果由于某种原因需要对原始功能进行引用(例如取消订阅),这一点很重要。

+0

+1 for'if(cb && cb.call)'...最好远离'typeof' –

+0

包装很好,但不能使用,因为'我需要添加包装到框架的任何功能,它可以接收函数作为参数... – Olegas

+0

我不喜欢他们,但你可以原型功能,所以你不必担心重写你的整个框架,只是你的客户端边调用。我会在一分钟内更新我的答案。 – Jeff

2

看来这是设计。

http://www.kilometer0.com/blog/code/internet-explorer-ie-cross-window-javascript-object-typeof-bug/

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

-1

打开子窗口后,可以将由父级创建的函数注册到其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> 
+0

主要问题是如何将一个在子窗口中创建的函数作为参数传递给父窗口函数。 – Olegas

+0

@ Olegas,你原来的帖子说:“父窗口在全局范围内有一些函数,必须用函数作为第一个参数调用它(它将用作回调函数)。”所以我希望问题是从子窗口调用在父窗口中定义的函数。在JavaScript中传递函数,实际上就像传递任何其他论点一样,这就是为什么我没有探究它。但没关系,似乎你已经解决了你的问题,你没有任何进一步的帮助。 –

+0

致电 - 不是问题。传递函数 - 是问题。在IE中,当它通过窗口边界时,它将'typeof'更改为'Object'。 – Olegas

2

即使你能得到这个对于现在的工作,我不会直接调用对方的功能被周围的能力两个浏览器窗口数更久,更长。即使您从场景中删除跨域问题,也存在太多的安全问题。

要做到这一点并确保它适用于所有浏览器,唯一的方法就是使用cross-document messaging APIs,它们在所有现代浏览器(包括IE8及更高版本)中均受支持。

当我需要解决这个问题时,我能找到的最详细的例子是window.postMessage article on MDN

它归结到是在一边通话张贴讯息,例如:

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()函数),然后使用跨文档消息传递系统发送事件,而不是您今天尝试使用的回调函数。

UPDATE


有可用来添加对老版本浏览器的postMessage支持polyfills。由于您没有进行跨域电话,我会从Ben Alman的Jquery postMessage plugin开始。我没有用过,但我用过Ben的杰出Jquery烧烤插件。如果支持,它应该回退到内置方法,并且只能在旧版浏览器中使用替代方法进行填充。它声称在IE7中工作...

如果Ben的插件没有做到这一点,那么你可以尝试easyXDM,但它看起来更容易设置。

+0

我认为这是一个最好的,标准兼容的工作区。但是这对我来说是一个问题,它在IE7中不被支持。无论如何,我认为你是赢家。 – Olegas

+0

我以前没有想过要看(我的时间截止日期),但在IE7中有一些polyfills提供这种方法。我更新了包含它们的答案。 –

+0

不错的lib,但它使用location.hash来传递消息......在我的环境中,散列是高度用于内部需求(如导航等)。但它可以被修改以满足我的需求,而不会破坏任何已经工作的东西。谢谢。 – Olegas

相关问题