postMessage源IFrame

时间:2013-03-11 01:01:12

标签: javascript html5 iframe cross-domain postmessage

我正在开发一个网站,其中包含使用postMessage调整到正确高度的跨域iframe。我唯一的问题是确定哪个iframe具有哪个高度。我目前设置的方式是,当一个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);

I帧:

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

是否有某种方法可以确定哪个iframe发送了message个事件?

6 个答案:

答案 0 :(得分:18)

我从这里找到了解决方案:How to share a data between a window and a frame in 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;
    }
}

答案 1 :(得分:17)

是的,您可以识别执行postMessage的IFRAME。有不同的情况:

  • 源IFRAME的同源网址(例如http://example.com/)作为接收消息的窗口:使用

    标识IFRAME

    myIFRAME.contentWindow == event.source

  • 源IFRAME向父HTML页面提供同源但相对网址(例如/myApp/myPage.html):使用

    标识IFRAME

    myIFRAME.contentWindow == event.source.parent

  • 源IFRAME有跨源网址(例如http://example.com/)与收到邮件的页面不同(例如http://example.org/):上述方法不起作用(比较总是false并且访问event.source的属性导致Access Denied错误),必须根据其原始域识别IFRAME;

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

为了管理这三种不同的情况,我使用以下内容:

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;
        }
    }
}

对于跨域网址,请注意,如果event.origin是多个IFRAME共有的域,则无法区分真正的来源。

有些人使用===代替==,但我没有发现这方面的任何差异,所以我使用的是最短的比较器。

此实施已经过测试,可在以下情况下使用:

  • MSIE 9
  • Firefox 17

作为替代方案(由Griffin建议),您可以使用具有唯一标识符(例如时间戳)的IFRAME src,并且IFRAME的Web应用程序将在发布的消息中发回该唯一标识符。虽然IFRAME标识会更简单,但这种方法需要修改IFRAME的Web应用程序(这并不总是可行)。这也可能导致安全问题(例如,IFRAME的Web应用程序试图猜测其他IFRAME应用程序的唯一标识符。)

答案 2 :(得分:4)

我有一个想法来解决这个问题。在您创建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);

答案 3 :(得分:1)

如果源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集合的成员的详细信息由Same Origin重新设置(即,您可能无法访问window.frames [i]

的frameElement或location属性

答案 4 :(得分:1)

以下是我的跨文章作品:

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);

在Chromium 64和Firefox 59中测试过。

答案 5 :(得分:-2)

该事件还应具有可与iframe“contentWindow”属性进行比较的属性“source”。