12

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 に存在します)

4

5 に答える 5

16

文字列化された関数をポストメッセージ イベント データとして渡すことを妨げるものは何もありません。次のような関数宣言の実装は簡単です。

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 バージョンをまとめることができるかどうかを確認しますが、jsfiddle を使用してホストすることは自明ではありません。

アップデート

約束どおり、機能する完全なモックアップ (以下のリンク)。正しいevent.originチェックがあれば、これは十分に侵入できないでしょうが、私たちのセキュリティチームがこのように本番環境に入れることは決してないだろうという事実を私は知っていますeval:)

オプションが与えられた場合、機能を 2 つのページにわたって正規化して、パラメトリック メッセージのみを渡す必要があるようにすることをお勧めします (つまり、関数ではなく引数を渡します)。ただし、これが推奨されるアプローチであるシナリオがいくつかあります。

親コード:

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) {}
}

iframe コード:

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 コード

于 2012-06-18T04:12:16.173 に答える
10

あなたは本当にできません。postMessageの(ドラフト) 仕様では、ネストされたオブジェクトや配列などの構造化オブジェクトについて説明していますが、[...] JavaScript 値 (文字列、数値、日付など) および [...] File Blob、FileList などの特定のデータ オブジェクト、および ArrayBuffer オブジェクトは、ほとんどのブラウザーで文字列 (もちろん JSON を含む) のみを許可します。詳細については、MDNまたはdev.operaを参照してください。それでも、少なくともスコープを保持するクロージャーとしてではなく、関数オブジェクトを送信することはできないと確信しています。

eval()したがって、親ウィンドウからコードを実行したい場合は、関数とiframe を文字列化することになります。ただし、アプリケーションが任意のコードの評価を許可する理由はわかりません (登録されたドメインからであっても)。(JSON-) 文字列コマンドを受け取り、独自のメソッドを呼び出すことができるメッセージ API を構築することをお勧めします。

于 2012-06-12T23:31:17.830 に答える
2

この場合、私は別のアプローチを試みます。なんで?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>

不便かもしれませんが、機能します。

于 2012-06-17T13:28:43.850 に答える
1

次の質問を拡張すると、関数を文字列化し、 を使用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できます。applybind

ただし、関数のスコープは動的であることに注意してください (つまり、関数内の非ローカル変数は、iframe ウィンドウで既に定義されている必要があります)。

あなたの場合、関数で使用しているjquery$変数は非ローカルです。したがって、iframe にはすでに jquery がロードされている必要があります。$親ウィンドウの jquery 変数は使用しません。

于 2012-06-18T05:36:56.180 に答える