我在一个网站上工作,使用postMessage将跨域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);
Iframe:
var updateHeight = function() {
if(window.parent) {
window.parent.postMessage($('.widget').outerHeight(), '*');
}
};
是否有办法确定哪个iframe发送了message
事件?
是的,您可以识别执行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具有跨源URL(例如
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;
}
}
}
对于跨域url,请注意,如果event.origin
是多个iframe共用的域,则我们无法区分真正的源。
有些人使用===
而不是==
,但我在这种情况下没有发现任何差异,所以我使用最短的比较器。
此实现已经过测试,并在以下条件下工作:
- MSIE 9
- Firefox 17
作为一个替代方案(Griffin建议),你可以使用带有唯一标识符(例如时间戳)的IFRAME src, IFRAME的web应用程序将在发布的消息中返回这个唯一标识符。虽然IFRAME标识会更简单,但这种方法需要修改IFRAME web应用程序(这并不总是可能的)。这也可能导致安全问题(例如,IFRAME的web应用程序试图猜测其他IFRAME应用程序的唯一标识符)。
我从这里找到了解决方案:如何在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;
}
}
,在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);
以下作品适合我跨产地:
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);
如果源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
}
重要的是要注意
windows .frames属性不受跨域策略的限制
无论源iframe嵌套得有多深
window.frames是窗口对象的集合,而不是iframe元素。
对window.frames集合成员属性的访问受同源限制(即你可能无法访问window.frames[i]的frameElement或location属性)
事件还应该有一个属性"source",可以与iframes的"contentWindow"属性进行比较。