【问题标题】:Communication between browser tabs/windows using JavaScript [duplicate]使用 JavaScript 在浏览器选项卡/窗口之间进行通信 [重复]
【发布时间】:2011-05-04 00:25:36
【问题描述】:

让 JavaScript 在同一浏览器的选项卡/窗口之间进行通信的最可靠方法是什么?例如,当 Tab 2 开始播放音频时,Tab 1 以某种方式知道这一点并可以暂停其播放器。

我正在建立一个带有音乐播放器的网站...所以目前如果您打开该网站的两个标签页,您可以同时在这两个标签页上播放音乐。 这显然很糟糕,所以我正在努力寻找解决方案。

【问题讨论】:

  • 无论如何,自动播放音频都是不好的。为什么不让用户点击“播放”按钮,如果遇到这种情况,手动暂停另一个选项卡?
  • 没有自动播放。但是,如果用户不必手动暂停另一个选项卡,那就太好了。例如,Youtube 会这样做(使用 Flash)
  • +1 好问题,我现在正在我的网站聊天应用程序中解决这个确切的问题。
  • 优质帖子。 5 年后仍然有用。

标签: javascript browser


【解决方案1】:

如需更现代的解决方案,请查看https://stackoverflow.com/a/12514384/270274

引用:

我坚持使用localStorage 问题中提到的共享本地数据解决方案。就可靠性、性能和浏览器兼容性而言,它似乎是最佳解决方案。

localStorage 在所有现代浏览器中都实现了。

storage 事件在 other 选项卡更改 localStorage 时触发。这对于交流来说非常方便。

参考:
http://dev.w3.org/html5/webstorage/
http://dev.w3.org/html5/webstorage/#the-storage-event

【讨论】:

  • 这比公认的解决方案要好。这不需要您经常检查新信息,没有延迟并且使您能够接收所有事件。
  • 我在 IE11 上遇到 localStorage 问题,请参阅这篇文章(我遇到了第 3 点)blogs.msdn.com/b/ieinternals/archive/2009/09/16/…,因此 cockie 解决方案更好(至少对于 IE)。我也尝试禁用 cockies 但它仍然有效(这是在 IE 11 上)。
  • @Anas:链接已失效。新网址:blogs.msdn.microsoft.com/ieinternals/2009/09/15/…
  • 这解决了很多问题(比 BroadcastChannel 实现的更广泛),但要小心,如果用户在同一个浏览器中打开 2 个“发件人”选项卡,您会有一些惊喜:拥有 1 个公共通信通道可能会带来一些惊喜
【解决方案2】:

更新到现代解决方案,出于历史原因将旧解决方案留在下面。

您可以使用广播频道 API 来发送和接收消息 https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API

// Connection to a broadcast channel
const bc = new BroadcastChannel('test_channel');

// Example of sending of a very simple message
// It doesn't have to be a string, it could be a JS object
bc.postMessage('This is a test message.');

接收消息:

// A handler that only logs the event to the console:
bc.onmessage = function (ev) {
  console.log(ev);
}

并关闭频道:

// Disconnect the channel
bc.close();

这是一种历史悠久的方法,请在现代浏览器中使用上述方法!

您可以使用 cookie 在浏览器窗口(以及选项卡)之间进行通信。

这里是发送者和接收者的例子:

发件人.html

<h1>Sender</h1>

<p>Type into the text box below and watch the text 
   appear automatically in the receiver.</p>

<form name="sender">
<input type="text" name="message" size="30" value="">
<input type="reset" value="Clean">
</form>

<script type="text/javascript"><!--
function setCookie(value) {
    document.cookie = "cookie-msg-test=" + value + "; path=/";
    return true;
}
function updateMessage() {
    var t = document.forms['sender'].elements['message'];
    setCookie(t.value);
    setTimeout(updateMessage, 100);
}
updateMessage();
//--></script>

receiver.html:

<h1>Receiver</h1>

<p>Watch the text appear in the text box below as you type it in the sender.</p>

<form name="receiver">
<input type="text" name="message" size="30" value="" readonly disabled>
</form>

<script type="text/javascript"><!--
function getCookie() {
    var cname = "cookie-msg-test=";
    var ca = document.cookie.split(';');
    for (var i=0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1,c.length);
        if (c.indexOf(cname) == 0) {
            return c.substring(cname.length, c.length);
        }
    }
    return null;
}
function updateMessage() {
    var text = getCookie();
    document.forms['receiver'].elements['message'].value = text;
    setTimeout(updateMessage, 100);
}
updateMessage();
//--></script>

【讨论】:

  • 我也想过这样的事情,但希望有比 cookies/setTimeout 更好的解决方案。然而,这可能只是唯一的解决方案。谢谢
  • 不要将字符串传递给setTimeout - 你这样做是在使用eval。相反,直接使用setTimeout(updateMessage, 100) 传递函数
  • 我也推荐使用setInterval()
  • 我在 IE11 上遇到了 localStorage 的问题,请参阅这篇文章(我遇到了第 3 点)blogs.msdn.com/b/ieinternals/archive/2009/09/16/…,因此 cockie 解决方案更好(至少对于 IE)。我也尝试禁用 cockies,但它仍然有效(这是在 IE 11、IE 10、IE 9 上)。
  • Tomáš Zato,请注意答案是从 2010 年开始的——当时并非所有浏览器都支持 HTML5,而且 IE6 和 IE7 的份额相当高。现在有更好的解决方案。
【解决方案3】:

还有一种名为Broadcast Channel API 的实验性技术,专为具有相同来源的不同浏览器上下文之间的通信而设计。您可以向另一个浏览器上下文发布消息并从另一个浏览器上下文接收消息,而无需对其进行引用:

var channel = new BroadcastChannel("foo");
channel.onmessage = function( e ) {
  // Process messages from other contexts.
};
// Send message to other listening contexts.
channel.postMessage({ value: 42, type: "bar"});

显然,这是一种体验式技术,尚不支持所有浏览器。

【讨论】:

  • 它不再是实验性的,即使 Edge 可能还没有实现它(它在 MDN 中标记为 ?
  • Edge 现在有了。
【解决方案4】:

我认为您不需要 cookie。每个文档的 JavaScript 代码都可以访问其他文档元素。因此您可以直接使用它们来共享数据。

您的第一个窗口 w1 打开 w2 并保存参考

var w2 = window.open(...)

在 w2 中,您可以使用 window 的 opener 属性访问 w1。

【讨论】:

  • 使用饼干?吃它们,享受吧!有一个更简单的方法!只需访问另一个窗口的 var。在 w1 中获得一个值 var,从 w2 whit window.opener.value 访问它!
  • 假设用户将它们全部打开。在这种情况下有没有类似的解决方案?
  • 大家都知道,这是错误的答案,正如@Ferdinak 已经试图说的那样。您没有对用户打开的选项卡的引用。
  • 有趣无关紧要。这首先是一个问答网站。人们来到这个页面是为了寻找提问者问题的答案。如果你想分享一些有趣的东西,你应该考虑写一个新的 wiki 风格的问题。
  • @jonas.ninja IMO 这个问题并不是 100% 清楚标签是如何打开的,所以即使它不是一个通用的答案,这也是一个完全有效的答案。
【解决方案5】:

您可以通过本地存储 API 执行此操作。请注意,这只适用于两个选项卡之间。您不能将发送者和接收者放在同一页面上:

在发件人页面上:

localStorage.setItem("someKey", "someValue");

在接收者页面上:

    $(document).ready(function () {

        window.addEventListener('storage', storageEventHandler, false);

        function storageEventHandler(evt) {
            alert("storage event called key: " + evt.key);
        }
    });

【讨论】:

  • 我打算使用这个方法,直到我发现 webbrowser 控件不会触发“storage”事件处理程序方法。不知道为什么。也许是错误。
  • 感谢您的解决方案。成就了我的一天。它不适用于 file:/// 协议,但适用于有效域。另一个类似的演示html5demos.com/storage-events
  • 您无需等待 DOM 准备就绪即可访问 localStorage
【解决方案6】:

在窗口(w1)下方打开另一个窗口(w2)。任何窗口都可以向/从另一个窗口发送/接收消息。因此,理想情况下,我们应该验证消息是否来自我们打开的窗口(w2)。

在 w1

var w2 = window.open("abc.do");
window.addEventListener("message", function(event){
    console.log(event.data);
});

在 w2(abc.do) 中

window.opener.postMessage("Hi! I'm w2", "*");

【讨论】:

【解决方案7】:

如果文档来源相同,甚至在 HTML5 之前就支持不同 JavaScript 执行上下文之间的通信。

如果没有,或者您没有引用其他 Window 对象,那么您可以使用 HTML5 引入的新 postMessage API。我在this Stack Overflow answer 中详细阐述了这两种方法。

【讨论】:

【解决方案8】:

如果窗口具有子父关系,您可以在窗口(选项卡式或非选项卡式)之间进行通信。

创建和更新子窗口:

<html>
<head>
<title>Cross window test script</title>
<script>
var i = 0;
function open_and_run() {
    var w2 = window.open("", "winCounter"); 
    var myVar=setInterval(function(){myTimer(w2)},1000);
}

function myTimer(w2) {
    i++;
    w2.document.body.innerHTML="<center><h1>" + i + "</h1><p></center>";
}
</script>
</head>
<body>
Click to open a new window 
<button onclick="open_and_run();">Test This!</button>    
</body>
</html>

子窗口可以使用parent 对象与生成它的父窗口进行通信,因此您可以从任一窗口控制音乐播放器。

在此处查看实际操作:https://jsbin.com/cokipotajo/edit?html,js,output

【讨论】:

  • 这里的一个问题是,我们不能(不被hacky)分享到我们同步视图的链接......
  • @yckart 有很多方法可以做到这一点,但我见过的最常见的方法是将字符串发送到另一个窗口中的输入框。您可以为值的更改创建一个事件侦听器。示例:stackoverflow.com/questions/9994120/…。我认为最好只在另一个窗口中调用一个 javascript 函数,并将 URL 传递给它。例如,来自子窗口的 或来自父窗口的
【解决方案9】:

我发现了一种使用 HTML5 本地存储的不同方式。我创建了一个包含 API 等事件的库:

sysend.on('foo', function(message) {
    console.log(message);
});
var input = document.getElementsByTagName('input')[0];
document.getElementsByTagName('button')[0].onclick = function() {
    sysend.broadcast('foo', {message: input.value});
};

https://github.com/jcubic/sysend.js

它将消息发送到所有其他页面,但不会发送到当前页面。

编辑

最新版本的库也支持广播通道通信,但在只支持本地存储的 IE11 中仍然有效。它还支持跨域通信(不同域),但需要一点代码。

最新的 API 还支持在同一页面上执行事件的 emit 函数。

即使是最新版本,也支持管理窗口,向特定窗口发送消息或获取窗口/标签列表。

【讨论】:

【解决方案10】:

使用 Flash,您可以在任何窗口、任何浏览器之间进行通信(是的,在运行时从 Firefox 到 Internet Explorer)...任何形式的 Flash 实例(@ 987654321@ 或 ActiveX)。

【讨论】:

  • 问题与 Flash 无关。
  • 这在大多数移动情况下都不起作用。
  • 公平地说,Flash 会按照 OP 的要求运行“一种形式的”Javascript
  • 它是如何工作的?机制是什么?浏览器不会阻止它工作吗?它是否通过互联网上的某些服务器?
  • 好的,他已经离开了大楼。其他人可以敲钟吗?
猜你喜欢
  • 2011-06-26
  • 2011-05-09
  • 1970-01-01
  • 2021-04-25
  • 2017-02-28
相关资源
最近更新 更多