22

我正在使用 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);

框架:

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

有什么方法可以识别哪个 iframe 发送了message事件?

4

6 回答 6

23

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

  • 源 IFRAME 与接收消息的窗口具有同源 URL(例如http://example.com/):使用 IFRAME 标识

    myIFRAME.contentWindow == event.source

  • 源 IFRAME 与父 HTML 页面具有同源但相对的 URL(例如/myApp/myPage.html):使用 IFRAME 标识

    myIFRAME.contentWindow == event.source.parent

  • 源 IFRAME 具有与接收消息的页面不同的跨域 URL(例如):上述方法不起作用(比较总是并且访问导致错误的属性)并且 IFRAME 必须基于识别在其原始域上;http://example.com/http://example.org/falseevent.sourceAccess Denied

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

对于跨域 URL,请注意,如果event.origin是多个 IFRAME 共有的域,我们无法区分真实来源。

有些人使用===而不是,==但我在这种情况下没有发现任何区别,所以我使用的是最短的比较器。

此实现已经过测试并在以下条件下工作:

  • 微博 9
  • 火狐 17

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

于 2013-12-05T15:38:41.030 回答
21

我从这里找到了解决方案:如何在 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;
    }
}
于 2013-08-16T06:51:23.470 回答
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);
于 2015-03-10T00:49:43.060 回答
3

以下适用于我的跨域:

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 中测试。

于 2018-03-31T21:55:16.780 回答
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 属性

于 2014-10-31T08:43:00.687 回答
-2

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

于 2013-05-15T00:45:34.370 回答