Cross Frame 与不同域进行交互

为何要用不同网域的 Iframe?
   通常较具规模的网站,会考虑这样的安全性问题:当要使用到第三方所提供的 JavaScript 时,得放置在与页面完全不同网域的 Iframe 中。例如在 Yahoo! 工作时,生活+ 与 UrMap 合作的地图就是放在另一个网域的 Iframe 中的。若不这样子做,UrMap 网站的 JavaScript 可以随时窜改 Yahoo! 网页的内容、甚至危害到使用者。

用不同网域的 Iframe 来放置第三方程式码的情形,简单来说就是:「我不信任你的程式、但我需要你的程式成为我网页中的一部分功能」(除了 Iframe,另外还有 ADSafe、Caja、Flash 等方式可以做到,但后三者都有一定的门槛。)

Same Origin Policy
   将第三方网页或程式码、用不同网域的 Iframe 放到你的网页上是一件非常简单的事情 (<iframe src="xxx"></iframe> 人人都会用)。但问题在于当两者网域不同时,JavaScript 是没有办法透过 parent, window.frames, top, opener 等方式存取的。这就是所谓 Same Origin Policy 的限制。

不能互动,就缺乏了弹性与应用的机会。例如刚刚提到 Yahoo! 生活+ 与 UrMap 的合作,生活+ 这边会希望在页面上点选了「餐厅」的连结时、就用 AJAX 取得伺服器的餐厅经纬度资讯、传递到放置地图不同网域的 Iframe 中、Iframe 接到讯息后就以 UrMap API 动态更新网页地图上的座标点。而这样的行为也会因 Same Origin Policy 无法直接达成。

Cross Document Messaging
    WHATWG 提出了 Cross Document Messaging(也是 HTML 5 的 Web Messaging)来解决此一问题。简单来说,你可以丢任何 String 到任何你网页上的 Window 物件,但是该 Window 物件要不要处理此 String,就是它自己的判断(会一併提供来源 Window 的 domain)。范例程式码如下:

A 网页 (http://yahoo.com/a.html) 放置了一个 name=urmap 的 iframe:

<iframe name="urmap" src="http://yahoomashup.com/urmap.html"></iframe>


A 网页想要传递 Hello World 的讯息给此 Iframe:

<script>
/* 我是 yahoo.com */
window.frames['urmap'].postMessage("Hello World!", "*");
</script>


Iframe 裡的 B 网页 (http://yahoomashup.com/urmap.html) 可以这样接到资料:
/* 我是 yahoomashup.com */
window.onmessage = function (e) {
    if (e.origin == "yahoo.com") { // 当来自 yahoo.com 时...
        alert(e.data); // 显示 "Hello World!"
    }
}


好消息是目前大多数的主流浏览器支援此方式,包括 Firefox 3, Google Chrome, Opera, IE8, IE9 皆可。坏消息则是 IE6 与 IE7 没有此功能。

Iframe In Iframe Hack

    如果此主题有任何 A-Grade 浏览器无法使用,那也没写这篇文章的必要。只提供半套作法就逊掉了(这也是我对大多数 HTML 5 的新技术感到冷感的原因,IE[6-8] must die 也只是开发者肝苦下喊爽的产物,现实世界是不能不管 IE 的使用者的)其实早在 2007 年就有一些非正规 Cross Frame 的解法。这篇文章是目前解说最详细的作法:Cross-Domain Communication with IFrames

其实原理相当简单,就是当 A 网页要传递讯息给 B 网页的(在 Iframe 中)时,先动态产生一个与 B 网页同网域的 Iframe,此动态 Iframe 内的 JavaScript 即可利用 window.frames 取得 B 网页及做任何修改。另外还可透过修改生成 Iframe 中的 URL hash (#)、让 JavaScript 可以依照不同参数做动作。这个作法的缺点是,你必须另外在接收端的网域放置一支 Proxy 的 HTML 档、作为动态 Iframe 的网址。

YAHOO.util.CrossFrame 函式库 (YUI2)


虽然原理很简单,但是要能够有系统的沟通、写程式就是一大挑战了、中间的小细节其实是很多的。幸好 Yahoo! 的前端工程师 Julien Lecomte 用 YUI 2 写了一个 CrossFrame Utility。

YAHOO.util.CrossFrame.send(
    "http://www.y.com/proxy.html",
    "frames['mashup']",
    "message"
);

YAHOO.util.CrossFrame.onMessageEvent.subscribe(
    function (type, args, obj) {
        var message = args[0];
        var domain = args[1];
        // Do something with the incoming message...
    }
);


   看到了吗?这边其实就是用 YAHOO.util.CrossFrame.send() 取代 HTML5 的 window.postMessage()、用 YAHOO.util.CrossFrame.onMessageEvent 取代 window.onmessage 事件。单一 API 介面、所有浏览器都能用!

用 YUI 3 改写为 Y.CrossFrame

    因为公司内用的是 YUI 3,有时间的话当然希望改写。另外就是 Julien 当时写那篇文章的时间点是 2007 年,postMessage 只有 Opera 有实作,所以 Julien 对于 Opera 以外浏览器都採用 Iframe in Iframe 的作法,效能差上许多。也因此我改为以 postMessage 为主、Iframe in Iframe 为辅的作法较有效率。

Y.CrossFrame.postMessage(
    "Hello World!",       // 要传递的讯息
    "frames['urmap']",  // 要传递的视窗(请用 String)
    {proxy:"http://yahoomashup.com/b.html"}  //  不支援 postMessage 的浏览器会用到的 proxy 网址
);


Y.Global.on("crossframe:message", function (message, domain, url) {
alert(message);
});

目前测试 IE6, IE7, IE8, FF3, Chrome, Opera 都没有问题(模拟器像是 IE Tester 无法使用)。希望对您有所帮助


原文引用地址  http://www.josephj.com/entry.php?id=338

你可能感兴趣的:(frame)