2012-05-08 37 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

相關問題