2012-05-08 8 views
9

2つのウィンドウがあり、1つは別のウィンドウから開かれていますので、「子」ウィ​​ンドウにopenerプロパティがあります。IEのあるウィンドウから別のウィンドウに関数を渡すことができません

親ウィンドウにはグローバルスコープ内の関数がいくつかあります。関数は最初の引数としてコールされる必要があります(コールバックとして使用されます)。

どちらのページには、子ウィンドウでは、同じドメインから開かれ、そう、私は(私はそう願って)任意の同一生成元ポリシーの制限はありませんが...

されている私は、この

if(window.opener) { 
    window.opener.myFunction(function() { ... }); 
} 
のようなコードを持っています

私はIEでそれを実行しようとするまで、すべてうまく動作します。このブラウザでは、myFunctionで受信された引数は、常にとタイプObjectである。 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

質問は次のようになります。これはstandarts準拠の動作

  1. のですか?
  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

'if(cb && cb.call)'のために... 'typeof'から離れているのが最も良い –

+0

ラッピングはいいですが使用できません。引数として関数を受け取ることができるフレームワークの関数にラップを追加する必要があります。 – Olegas

+0

私はそれらを好きではありませんが、プロトタイプ関数を使うことができますので、フレームワーク全体を書き直す心配はありませんサイドコール。私は1分で私の答えを更新します。 – 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

あなたは、これが今の仕事を得ることができたとしても、私は直接の周りにあることをお互いに機能を呼び出すために2つのブラウザウィンドウのための能力にカウントされないだろうもっと長く。シナリオからドメイン間の問題を取り除いても、あまりにも多くのセキュリティ上の問題があります。

これを実行し、すべてのブラウザで動作することを保証する唯一の唯一の方法は、IE8以上を含むすべての最新のブラウザでサポートされているcross-document messaging APIsを使用することです。

私はこの問題を解決するために必要なときに、私は見つけることができる最も詳細な例は、この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 BBQプラグインを使用した。サポートされている場合は組み込みのメソッドに戻り、古いブラウザでは代替のメソッドのみを使用します。これはベンのプラグインがトリックをしない場合は、あなたがeasyXDMを試みることができる

... IE7で動作するように主張したが、それは設定を取得するためにはもう少し複雑になります。

+0

私はこれが最高の、standart準拠のworkaraoundだと思う。しかし、これは私にとってIE7ではサポートされていない問題です。とにかく、あなたは勝者だと思います。 – Olegas

+0

私は以前は見ていないと思っていました(私は期限を迎えていました)が、IE7にこの方法を提供するいくつかのポリフィルがあります。私はそれらを含める答えを更新しました。 –

+0

Nice libですが、メッセージを渡すのにlocation.hashを使用しています...私の環境では、ハッシュは内部のニーズ(ナビゲーションなど)によく使われます。しかし、それは私のニーズを満たすために変更することができ、すでに働いているものを壊さないようにすることができます。どうも。 – Olegas

関連する問題