限制解决方案是100%由JavaScript客户端实现的,在HTML5之前,有许多棘手的hacks方式,例如:
URL长轮询:容器页面A改变iframe页面B的URL hash,B周期性的检查hash,一旦hash发生变化,它将根据约定的hash值采取行动。[随便说一下,这个模式可以被改进为非轮询的HTML onchashchange event]
CorssFrame,一种跨文档和跨域的安全通讯机制
窗口大小监视:一旦改变iframe的窗口大小,包含的窗口触发onresize事件,采取相应的行动。Google Mapplets采用了这种方式。
嗯,这些方式我个人都不喜欢,也许是不优雅,或者侵犯了DOM元素的原始功能,或者是太复杂。我相信许多人也不喜欢它们,我打赌甚至这些模式的发明者也不喜欢它们。这就是WHATWG在HTML5中创建跨域通讯:Web Messaging的原因。
作为一个HTML5的疯狂倡导者,我非常喜欢它,完全的客户端通讯,不影响服务器端,高效的,安全的(至少在理论上)
"Child" 可以是iframe 或通过window.open触发的弹出窗口, "parent page" A 的源码如下:
<iframe id="ifr" src="http://domainB.com/B.htm" onload="sendCommand();"> No frame! </iframe> <script type="text/javascript"> function sendCommand() { var ifr = document.getElementById("ifr"); ifr.contentWindow.postMessage("Hello", "http://domainB.com"); } </script> Notice, make sure to post message only when the iframe is loaded, otherwise thecontentWindowwill be still in the same domain with the container page. B页面源码如下: <input type="button" value="Cross domain call" onclick="sendMsg();" /> <script> window.addEventListener("message", receiveMessage, false); function receiveMessage(evt) { console.log("Page B received message from origin: %s.", evt.origin); console.log("Event data: %s", evt.data); //evt.source will be a window who sent the message, //it can be used to post message to it // Take action(s) } </script>
示例代码是一个单向消息流: 从父页面到子页面发消息(iframe), 当然双向消息流也是可以的, 就像父亲管理儿子, 子页面发消息到容器窗体, 只是这回需要调用 "parent.postMessage".
function receiveMessage(evt) { evt.source.postmessage("Hello caller"); // or parent.postmessage("Hello parent"); }
简单的说 HTML5 Web消息机制就是JavaScript开放给浏览器的API, 以便在不同的内容间通讯 browsing context, 当JavaScript 从其中一个 tab/window 向另一个tab/window发消息时, 浏览器会定位指定域中的 tab/window , 然后触发消息事件 MessageEvent (继承自 DOMEvent)把消息发送到目标tab/window, 所以一旦源 tab/window已经触发了消息事件,它会受收到提示信息,最终通过MessageEvent.data传送消息.
我在自己机器上试了下,改了一下hosts 文件,加上了Container.com, DomainA.com, DomainB.com 和 DomainC.com ,都指向 127.0.0.1:
127.0.1.1 Container.com
127.0.0.1 DomainA.com
127.0.0.1 DomainB.com
127.0.0.1 DomainC.com
然后准备了一个Containerpage :
<h3>HTML5 Cross-Domain post message demo</h3> <p id="infoBar"> </p> <div id="wrapperA"> <input type="text" id="txtA" /> <input type="button" value="Post Message" onclick="postMsgToIfr('A');" /> <iframe id="ifrA" src="http://DomainA.com/A.htm"></iframe> </div> <div id="wrapperB"> <input type="text" id="txtB" /> <input type="button" value="Post Message" onclick="postMsgToIfr('B');" /> <iframe id="ifrB" src="http://DomainB.com/B.htm"></iframe> </div> <div id="wrapperC"> <input type="text" id="txtC" /> <input type="button" value="Post Message" onclick="postMsgToIfr('C');" /> <iframe id="ifrC" src="http://DomainC.com/C.htm"></iframe> </div> <div style="CLEAR: both"> </div> <script type="text/javascript"> window.addEventListener("message", receiveMessage, false); var infoBar = document.getElementById("infoBar"); function receiveMessage(evt) { infoBar.innerHTML += evt.origin + ": " + evt.data + ""; } function postMsgToIfr(domain) { switch (domain) { case "A": var ifr = document.getElementById("ifrA"); ifr.contentWindow.postMessage(document.getElementById ("txtA").value, "http://DomainA.com"); break; case "B": var ifr = document.getElementById("ifrB"); ifr.contentWindow.postMessage(document.getElementById ("txtB").value, "http://DomainB.com"); break; case "C": var ifr = document.getElementById("ifrC"); ifr.contentWindow.postMessage(document.getElementById ("txtC").value, "http://DomainC.com"); break; default: throw ("No such domain!"); } } </script>
然后是3个页面: A.htm放在 DomainA.com, B.htm 放在DomainB.com, C.htm 放在 DomainC.com, 实际上都是放在 C:\inetpub\wwwrooot, 然后在浏览器里输入DomainA/B/C.com , A/B/C 页面的代码如下:
<h4>DomainA/A.htm1</h4> <input type="button" value="Cross domain call" onclick="doClick();" /> <div id="d"></div> <script> window.addEventListener("message", receiveMessage, false); function doClick() { parent.postMessage("Message sent from " + location.host, "http://container.com"); } var d = document.getElementById("d"); function receiveMessage(evt) { d.innerHTML += "Received message \"<span>" + evt.data + "</span>\" from domain: " + evt.origin + ""; } </script>
为了在不同的浏览器上下文下支持独立的通讯。HTML5引入了消息通道独立的发送消息。官方的定义如下:
通讯通道的机制实现了一个双向的管道,每端有一个端口。消息从一个端口发送到另一个端口,反之亦然。消息是异步的,并作为DOM事件发送。
我花费了大约半天的时间研究消息通道,最终实现了它。我的代码如下:
容器页的源码
<iframe id="ifr" src="http://wayneye.me/WebProjects/HRMS/Opener.html" önload="initMessaging()"></iframe> <input type="button" value="Post Message" onclick="postMsg();" /> <div id="d"></div> <script> var d = document.getElementById("d"); var channel = new MessageChannel(); channel.port1.onmessage = function (evt) { d.innerHTML += evt.origin + ": " + evt.data + ""; }; function initMessaging() { var child = document.getElementById("ifr"); child.contentWindow.postMessage('hello', 'http://wayneye.me', [channel.port2]); } function postMsg() { channel.port1.postMessage('Message sent from ' + location.host); } </script> iframe页的源码 <div id="info"></div> <input type="button" value="Post Message" önclick="postMsg();" /> <script> var info = document.getElementById("info"); var port = null; window.addEventListener("message", function (e) { console.log(e); if(e.ports && e.ports.length > 0) { port = e.ports[0]; port.start(); port.addEventListener("message", function (evt) { info.innerHTML += "Received message \"" + evt.data + "\" from domain: " + evt.origin + ""; }, false); } }, false); function postMsg() { if(port) { port.postMessage("Data sent from " + location.host); } } </script>
整个过程如下:
容器页(A)内嵌一个iframe,iframe的src指向不同域的页面(B)
一旦iframe加载,容器页发送一个带MessagePortArray的消息到页面B
页面B接收消息,数组包含了MessagePort对象的列表
页面B在端口实例中注册onmessage事件
页面B调用port.postmessage通过已建立的消息通道发送消息到页面A
目前,好像仅有Opera能正确的支持消息通道,Google Chrome和IE10平台预览版2在发送ports array的时候失败了,Safari不能触发onmessage事件。Firefox 7.0 beta版不支持MessageChannel对象。
注意:端口也能在HTML5 Web Workers之间通讯
还有一点(借用Steve Jobs 的话 ), 在使用Message Channel 时一定要注意,当使用完后一定记得要关闭通道,否则两个页面间会有强引用, W3 官方说明如下:
使用者记得要明确关闭MessagePortobjects以便其资源重新分配. 如若不然,或造成内存的大量浪费.HTML5 Web Messaging
window.postMessage - MDN Docs
HTML5 Demo: postMessage (cross domain)
HTML5's window.postMessage AP
Internet Explorer 10 Platform Preview: HTML5
http://ithelp.ithome.com.tw/question/10057709
原文链接:http://www.codeproject.com/Articles/248264/HTML-WebMessaging-Experiment