有關跨域通信,可以叫跨域請求,跨域數據訪問,想必大家在工作或多或少地接觸到,網上也能搜出一大羅出來。我的解決方法與他們的不同之處是不使用代理頁。
確切來說,在IE67無法使用postMessage的情況,我們通常需要動態生成一個隱藏iframe來加載通信頁,而它可能是跨域的。window.name的逆天之處在於,iframe.contentWindow.name是共用,即便因為URL的切換導致里面的不斷改變,如果沒有人為修改它,一直就是那個樣子。但不同域的情況下,我們仍然不能訪問iframe中的window.name,這時我們再把iframe切換成本域的頁面就行了。這就是window.name通信的實現機制。
雖說生成一個代理頁沒什么難度,更何況它可以是空白的頁面。但作為一個組件,這也算是一種約束。約束越少越好。經我研究,有兩個URL可以算是本域的永久地址,一個是/favicon.ico(IE下不行),另一個是about:blank。我們就用about:blank作為代理頁!
下面是我的實現:
//by 司徒正美 2010 7 30 (function() { //數據發送頁的URL,回調,加用於opera的延時時間(可選) var UIloader = function( url, callback, operatime){ if(typeof url === "string" && typeof callback == "function"){ url += (url.indexOf('?') > 0 ? '&' : '?') + '_time'+ new Date * 1; operatime = typeof operatime === "number" ? operatime : 3000; var el = document.createElement('iframe'), data; function receive(e){ e = e || event; el._state = 2; callback(e.data) if(window.removeEventListener){ window.addEventListener('message', receive, false) }else{ window.detachEvent('onmessage', receive); } body.removeChild(el) } if(window.addEventListener ){ window.addEventListener('message', receive, false) }else{ window.attachEvent('onmessage', receive); } el.style.display = "none"; el._state = 0; var body = document.body || document.documentElement; body.insertBefore( el, body.firstChild ); ;(function( node, type, fn ) { if ( window.VBArray ) { node.attachEvent('on' + type, fn); } else { node.addEventListener(type, fn, false); } })(el, 'load', function eee() { if(el._state === 1 ) { try { data = el.contentWindow.name; } catch(e) {} el._state = 2; callback(data) callback = function(){} body.removeChild(el) } else if(el._state === 0) { setTimeout(function(){ el._state = 1; el.contentWindow.location.replace("about:blank") }, (window.opera ? operatime : 31) )//必須等iframe的資源都加載完才跳轉,opera顯然load觸發時機不對 } }); el.src = url; }else{ throw "arguments error" } } UIloader("http://www.cnblogs.com/rubylouvre/archive/2012/07/28/2613565.html",function(a){ window.console && console.log(a+"!!!!!!!!!!!!!") }) })();
現場實例觀摩:數據請求頁,數據發送頁,具體代碼見頁面源碼!然后我們就會在數據請求頁的控制台看到打印日志了!
再說回來,為什么叫做UIloader呢,因為一般的跨域數據傳送,使用JSONP就夠了,非常輕便!但對於UI組件,比如grid,它通常包括一個體積也夠為嚇人的JS文件,還有一個樣式表,如果不想通過字符串拼接來渲染界面,我們還可能用到前端模板,當然還有圖片什么的。撇開圖片不談,JS,前端模板,樣式我們都可以統統整到一個JS文件里面的,但會顯然很亂,尤其是大段的CSS樣式,HTML字段,這是不是用HTML來放置它們比較好呢。我們用一個HTML來放置它們,樣式表寫到style標簽中,HTML寫到一個DIV上,腳本寫到一個script中。那么它們它們拼成一個對象:
var data = { html: div.innerHTML, js: script.text, css: style.cssText } window.name = data if(window.postMessage){ window.parent.postMessage(data, "*"); }
不過需要注意的是postMessage在IE下只能傳字符串,我們只好在發送頁統一返回字符串,用JSON.stringify轉換一下就行了,取回來再用JSON.parse變成對象!