JavaScript:监听特定iframe的postMessage事件

38

我在页面中有多个iframe。现在我有一个用于页面的message事件监听器,可以获取所有iframe的消息。我有一个解决方法来确定消息来自哪个iframe。

我想为每个iframe单独创建事件监听器。这是否可行?


不要认为这是不可能的。窗口可以从任何地方接收“消息”事件。如果您有解决方法,那可能就可以了。 - Paul Grime
1
只是出于好奇,你是如何实现你的解决方法的? - zer00ne
1
我知道这个问题很老了,但如果您可以控制框架内容,您可以使用通道消息API为每个框架设置不同的通道,并为每个通道设置单独的事件监听器。 - robyoder
7个回答

103

你需要监听全局的window对象上的message事件,但是你可以使用MessageEventsource属性来过滤源iframe。

例如:

const childWindow = document.getElementById('test-frame').contentWindow;
window.addEventListener('message', message => {
    if (message.source !== childWindow) {
        return; // Skip message in this event listener
    }

    // ...
});

它不总是对我起作用,不知道为什么。所以,我不得不使用message.target[0]而不是message.source,然后它就像魔法般地工作了...... - Iulian Pinzaru

11

如果每个 iframe 的 src 属性都是唯一的,那么您可以尝试以下操作:

在子级上:

function sendHeight() {
  // sends height to parent iframe
  var height = $('#app').height();
  window.parent.postMessage({
    'height': height,
    'location': window.location.href
  }, "*");
}

$(window).on('resize', function() {
  sendHeight();
}).resize();

在父级元素上:

$(window).on("message", function(e) {
    var data = e.originalEvent.data;
    $('iframe[src^="' + data.location + '"]').css('height', data.height + 'px');
});

使用postMessage(),子级向iframe父级发送其高度和URL。然后,父级侦听该事件,通过该URL获取iframe并将其高度设置为相应值。


我想我可以去掉高度位,是吗? - Jan Werkhoven
我认为楼主只是希望为每个iframe设置独立的事件监听器,但这是无用的,因为对任何iframe设置多个事件监听器将是重复的,因为它们是相同的事件。任何重复的事件监听器都只是浪费而已,因为只有一个会起作用,而任何重复的监听器都将被忽略。 - zer00ne

9

实际上你是可以的。 给每个iframe添加一个唯一的名称属性。 iframe名称被传递到contentWindow中。 因此,在iframe窗口内,window.name是iframe的名称,您可以轻松地将其发送到post message中。


4

您可以使用e.originalEvent.origin来识别iframe。

在iframe子页面上:

window.parent.postMessage({
  'msg': 'works!'
}, "*");

在iframe父页面:
Javascript
window.addEventListener('message', function(e) {
  console.log(e.origin); // outputs "http://www.example.com/"
  console.log(e.data.msg); // outputs "works!"
  if (e.origin === 'https://example1.com') {
    // do something
  } else if (e.origin === 'https://example2.com'){
    // do something else
  }
}, false);

jQuery

$(window).on('message', function(e) {
  ...
}, false);

因此,origin包含了触发postMessage()的协议和域名信息。它不包括URI。这种技术假定所有的iframe都有一个唯一的域名。


1
e.originalEvent.origin返回的是一个序列化字符串吗?我对jQuery并不是很熟悉,我所找到的只是它返回了由jQuery方便地封装在对象事件中的某些属性。因此,origin实际上可以显示事件的location.protocol + location.hostname吗? - zer00ne

1
不,这是不可能的。最好的方法是有一个单一的处理程序,根据消息发送者的来源将接收到的消息路由到辅助处理程序。

9
可以通过为每个iframe设置唯一的名称属性来实现。然后在iframe内部,window.name即为iframe的名称,并可以通过postMessage发送。 - Maciej Krawczyk
上面的评论应该是一个答案,它是最好的解决方案。 - kursus
(目前)最高评分的答案表明,可以通过对事件应用源过滤器来实现。 - BenjaminH

1
检测消息来源的一种方法是通过检查哪个iframe被聚焦或者对于我的特定场景,哪个iframe是可见的。

0

我实现了一个iframe代理。嵌套的iframe(即内部嵌套)。 每个iFrame代理都创建了自己独特的ID。 然后,从子iframe发送到父级的每条消息都会增加一个iframe代理ID字段。 在父级中,您可以将来自iframe代理的每条消息路由到其专用处理程序中。 这种机制完美地分离了iframes。


2
你应该展示一些代码(或至少是伪代码)来解释那个问题。 - Ironkey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接