web-dev-qa-db-ja.com

postMessageソースIFrame

PostMessageを使用して正しい高さにサイズ変更されたクロスドメインiframeを備えたWebサイトで作業しています。私が抱えている唯一の問題は、どのiframeがどの高さであるかを特定することです。私が現在設定している方法は、1つのiframeがその高さを親に送信すると、すべてのiframeの高さが変更されるというものです。

親:

var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";

eventer(messageEvent, function(e) {
    $('iframe').height(e.data);
}, false);

Iframe:

var updateHeight = function() {
    if(window.parent) {
        window.parent.postMessage($('.widget').outerHeight(), '*');
    }
};

どのiframeがmessageイベントを送信したかを特定する方法はありますか?

21
skimberk1

私はここから解決策を見つけました: JavaScriptでウィンドウとフレームの間でデータを共有する方法

親:

var frames = document.getElementsByTagName('iframe');
for (var i = 0; i < frames.length; i++) {
    if (frames[i].contentWindow === event.source) {
        $(frames[i]).height(event.data); //the height sent from iframe
        break;
    }
}
19
Jack

はい、postMessageを実行したIFRAMEを特定できます。さまざまな状況があります。

  • ソースIFRAMEには、メッセージを受信するウィンドウとして同じオリジンURL(例:_http://example.com/_)があります。IFRAMEは次を使用して識別されます。

    _myIFRAME.contentWindow == event.source_

  • ソースIFRAMEには親HTMLページへの同じ起源ですが相対URL(例:_/myApp/myPage.html_)があります:IFRAMEは次を使用して識別されます

    _myIFRAME.contentWindow == event.source.parent_

  • ソースIFRAMEには、メッセージを受信するページ(例:_http://example.com/_)とは異なるクロスオリジンURL(例:_http://example.org/_)があります:上記の方法は機能しません(比較は常にfalseであり、_event.source_のプロパティにアクセスすると_Access Denied_ errors)になり、IFRAMEはそのオリジンドメインに基づいて識別される必要があります。

    myIFRAME.src.indexOf(event.Origin)==0

これらの3つの異なる状況を管理するために、私は以下を使用しています。

_var sourceFrame = null; // this is the IFRAME which send the postMessage
var myFrames = document.getElementsByTagName("IFRAME");
var eventSource = event.source; // event is the event raised by the postMessage
var eventOrigin = event.Origin; // Origin domain, e.g. http://example.com

// detect the source for IFRAMEs with same-Origin URL
for (var i=0; i<myFrames.length; i++) {
    var f = myFrames[i];
    if (f.contentWindow==eventSource || // for absolute URLs
        f.contentWindow==eventSource.parent) { // for relative URLs
        sourceFrame = f;
        break;
    }
}

// detect the source for IFRAMEs with cross-Origin URL (because accessing/comparing event.source properties is not allowed for cross-Origin URL)
if (sourceFrame==null) {
    for (var i=0; i<myFrames.length; i++) {
        if (myFrames[i].src.indexOf(eventOrigin)==0) {
            sourceFrame = myFrames[i];
            break;
        }
    }
}
_

クロスドメインURLの場合、_event.Origin_が複数のIFRAMEに共通のドメインである場合、実際のソースを区別できないことに注意してください。

_===_の代わりに_==_を使用する人もいますが、このコンテキストで違いは見つからなかったため、最短のコンパレータを使用しています。

この実装はテスト済みであり、以下で機能します。

  • MSIE 9
  • Firefox 17

別の方法として(Griffinが提案)、一意の識別子(タイムスタンプなど)を持つIFRAME srcを使用すると、IFRAMEされたWebアプリケーションが投稿されたメッセージでこの一意の識別子を送り返します。 IFRAMEの識別は簡単ですが、このアプローチではIFRAMEのWebアプリケーションを変更する必要があります(これは常に可能であるとは限りません)。これはセキュリティの問題にもつながる可能性があります(たとえば、IFRAMEのWebアプリケーションが他のIFRAMEアプリケーションの一意の識別子を推測しようとします)。

19
Julien Kronegg

私はこの問題を解決するためのアイデアを持っています。 iframeを作成するときに、iframeに名前/ IDを付けます。 。

そして、iframe内のスクリプトで、次のようなオブジェクトとしてメッセージを送信します。

window.parent.postMessage({"height" : $('.widget').outerHeight(), "frmname" : window.name}, '*');

親リスナーでは、

eventer(messageEvent, function(e) {`enter code here`
    $(e.data.frmname).height(e.data.height);
}, false);
4
aha

以下は、クロスオリジンで機能します。

window.addEventListener('message', function (event) {
  if (event.data.size) {
    Array.prototype.forEach.call(document.getElementsByTagName('iframe'), function (element) {
      if (element.contentWindow === event.source) {
        element.style.height = `${event.data.size.height}px`;
      }
    });
  }
}, false);

Chromium64およびFirefox59でテスト済み。

2
Mitar

ソースiframeが複数の親iframeにネストされている場合は、各iframeのwindow.framesプロパティを繰り返し処理して、messageEvent#sourceプロパティと比較する必要があります。

たとえば、メッセージがこのDomのiframe#level3によって生成された場合です。

<iframe Id=level1>
   <iframe Id=level2>
       <iframe Id=level3 />
   </iframe>
</iframe>

を使用して、現在のウィンドウで祖先iframeのインデックスを見つけることができるはずです。

FindMe = event.source
FrameIndex = find(window)
frames[FrameIndex].frameElement ==     getElByTagName(iframe)[FrameIndex] 

function find(target){
    for (i=0; I< target.frames.length; i ++)
       if(target.frames[i] == FindMe ||   find(target.frames[i]))
           return i
    return false 
}

そのことに注意することが重要です

Window.framesプロパティは、クロスドメインポリシーによって制限されていません

この手法は、ソースiframeがどの程度深くネストされているかに関係なく機能します

window.framesは、iframe要素ではなくウィンドウオブジェクトのコレクションです。

window.framesコレクションのメンバーのプロパティへのアクセスは同一生成元によって制限されます(つまり、window.frames [i]のframeElementまたはlocationプロパティにアクセスできない場合があります。

1
server herder