有關跨域通訊,可以叫跨域請求,跨域資料訪問,想必大家在工作或多或少地接觸到,網上也能搜出一大羅出來。我的解決方案與他們的不同之處是不使用代理頁。
確切來說,在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變成對象!