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