很多架構存在父子關係,操作起來十分麻煩,很多同學經常出現這樣悲催的代碼:
| 代碼如下 |
複製代碼 |
|
window.parent.document.getElementById("main")
.contentWindow.document.getElementById('input').value =
document.getElementById('myIframe')
.contentWindow.document.getElementById('s0').value; |
看一個我自己做的執行個體
iframe的調用包括以下幾個方面:(調用包含html dom,js全域變數,js方法)
首頁面調用iframe;
iframe頁面調用首頁面;
首頁面的包含的iframe之間相互調用;
主要知識點
1:document.getElementById("ii").contentWindow 得到iframe對象後,就可以通過contentWindow得到iframe包含頁面的window對象,然後就可以正常訪問頁面元素了;
2:$("#ii")[0].contentWindow 如果用jquery選取器獲得iframe,需要加一個【0】;
3:$("#ii")[0].contentWindow.$("#dd").val() 可以在得到iframe的window對象後接著使用jquery選取器進行頁面操作;
4:$("#ii")[0].contentWindow.hellobaby="dsafdsafsdafsdafsdafsdafsadfsadfsdafsadfdsaffdsaaaaaaaaaaaaa"; 可以通過這種方式向iframe頁面傳遞參數,在iframe頁面window.hellobaby就可以擷取到值,hellobaby是自訂的變數;
5:在iframe頁面通過parent可以獲得首頁面的window,接著就可以正常訪問父親頁面的元素了;
6:parent.$("#ii")[0].contentWindow.ff; 同級iframe頁面之間調用,需要先得到父親的window,然後調用同級的iframe得到window進行操作;
源碼
main.html
| 代碼如下 |
複製代碼 |
?<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>顯示圖表</title>
<script src="/jquery-1.7.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
var gg="dsafdsafdsafdsafsdaf";
function ggMM() {
alert("2222222222222222222222222222222");
}
function callIframeMethod() {
//document.getElementById("ii").contentWindow.test();
$("#ii")[0].contentWindow.test(); //用jquery調用需要加一個[0]
}
function callIframeField() {
alert($("#ii")[0].contentWindow.ff);
}
function callIframeHtml() {
alert($("#ii")[0].contentWindow.$("#dd").val());
//alert($("#ii")[0].contentWindow.document.getElementById("dd").value);
//alert($("#ii")[0].contentWindow.document.getElementById("dd").value);
}
function giveParameter() {
$("#ii")[0].contentWindow.hellobaby="dsafdsafsdafsdafsdafsdafsadfsadfsdafsadfdsaffdsaaaaaaaaaaaaa";
}
</script>
</head>
<body>
<a href="#" onClick="giveParameter();">參數傳遞</a>
<a href="#" onClick="callIframeMethod();">調用子iframe方法</a>
<a href="#" onClick="callIframeField();">調用子iframe變數</a>
<a href="#" onClick="callIframeHtml();">調用子iframe組件</a></br>
<iframe id="ii" src="frame.htm" width="100%" frameborder="0"></iframe>
<iframe id="new" src="newFrame.htm" width="100%" frameborder="0"></iframe>
</body>
</html>
|
frame.htm
| 代碼如下 |
複製代碼 |
?<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>顯示圖表</title>
<script src="scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
var ff="adfdasfdsafdsafdsaf";
function test() {
alert($("#dd").val());
}
function callMainField() {
alert(parent.gg);
}
function callMainMethod() {
parent.ggMM();
}
function callMainHtml() {
alert(parent.$("#ii").attr("id"));
}
function getParameter() {
alert(window.hellobaby);
}
</script>
</head>
<body>
<a href="#" onClick="getParameter();">接受參數</a>
<a href="#" onClick="callMainMethod();">調用子iframe方法</a>
<a href="#" onClick="callMainField();">調用主視窗變數</a>
<a href="#" onClick="callMainHtml();">調用子iframe組件</a>
<input id="dd" type="text" value="1111111111"/>
</body>
</html>
|
兄弟iframe頁面 newIframe.htm
| 代碼如下 |
複製代碼 |
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>顯示圖表</title>
<script src="/jquery-1.7.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
function callLevelFrame() {
var ff=parent.$("#ii")[0].contentWindow.ff;
alert(ff);
}
</script>
</head>
<body>
<a href="#" onClick="callLevelFrame();">調用兄弟iframe</a>
<input id="nn" type="text" value="sdafsdfsa"/>
</body>
</html>
|
其實這個問題可以被大大的簡化,架構應用中有一個固定不變的視窗叫window.top,如果我們把資料緩衝到這個頁面,其下所有架構都可以擷取到,無論子頁面如何變幻。不需要採用Cookie,也不需要採用什麼HTML5本機資料庫策略,你只需要每個頁面引用一個js檔案,內容如下:
| 代碼如下 |
複製代碼 |
|
var share = {
/**
* 跨架構資料共用介面
* @param {String} 儲存的資料名
* @param {Any} 將要儲存的任意資料(無此項則返回被查詢的資料)
*/
data: function (name, value) {
var top = window.top,
cache = top['_CACHE'] || {};
top['_CACHE'] = cache;
return value !== undefined ? cache[name] = value : cache[name];
},
/**
* 資料共用刪除介面
* @param {String} 刪除的資料名
*/
removeData: function (name) {
var cache = window.top['_CACHE'];
if (cache && cache[name]) delete cache[name];
}
};
|
這個寥寥數行的方法可以共用任意類型的資料供各個架構頁面讀取,它與頁面名稱、層級毫無關係,就算哪天架構頁面層級被修改,你也完全不用擔心,它可正常工作。
例如,如我們可以在A頁面存入共用資料:
| 代碼如下 |
複製代碼 |
|
share.data(‘myblog’, ‘http://www.planeart.cn’);
share.data(‘editTitle’, function (val) {
document.title = val;
}); |
然後某架構頁面任意取A頁面的資料
| 代碼如下 |
複製代碼 |
|
alert(‘我的部落格地址是: ‘ + share.data(‘myblog’);
var editTitle = share.data(‘editTitle’);
editTitle(‘我已經擷取到了資料’); |
執行個體誇網域作業
document.domain+iframe的設定
對於主域相同而子域不同的例子,可以通過設定document.domain的辦法來解決。具體的做法是可以在http://www.a.com/a.html和http://script.a.com/b.html兩個檔案中分別加上document.domain = ‘a.com’;然後通過a.html檔案中建立一個iframe,去控制iframe的contentDocument,這樣兩個js檔案之間就可以“互動”了。當然這種辦法只能解決主域相同而次層網域不同的情況,如果你異想天開的把script.a.com的domian設為alibaba.com那顯然是會報錯地!代碼如下:
www.a.com上的a.html
| 代碼如下 |
複製代碼 |
document.domain = 'a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://script.a.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
var doc = ifr.contentDocument || ifr.contentWindow.document;
// 在這裡操縱b.html
alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
}; |
script.a.com上的b.html
document.domain = 'a.com';這種方式適用於{www.kuqin.com, kuqin.com, script.kuqin.com, css.kuqin.com}中的任何頁面相互連信。
備忘:某一頁面的domain預設等於window.location.hostname。主網域名稱是不帶www的網域名稱,例如a.com,主網域名稱前面帶首碼的通常都為次層網域或多級網域名稱,例如www.a.com其實是次層網域。 domain只能設定為主網域名稱,不可以在b.a.com中將domain設定為c.a.com。
問題:
1、安全性,當一個網站(b.a.com)被攻擊後,另一個網站(c.a.com)會引起安全性漏洞。
2、如果一個頁面中引入多個iframe,要想能夠操作所有iframe,必須都得設定相同domain。
2、動態建立script
雖然瀏覽器預設禁止了跨域訪問,但並不禁止在頁面中引用其他域的JS檔案,並可以自由執行引入的JS檔案中的function(包括操作cookie、Dom等等)。根據這一點,可以方便地通過建立script節點的方法來實現完全跨域的通訊。具體的做法可以參考YUI的Get Utility
這裡判斷script節點載入完畢還是蠻有意思的:ie只能通過script的readystatechange屬性,其它瀏覽器是script的load事件。以下是部分判斷script載入完畢的方法。
| 代碼如下 |
複製代碼 |
js.onload = js.onreadystatechange = function() {
if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
// callback在此處執行
js.onload = js.onreadystatechange = null;
}
}; |
3、利用iframe和location.hash
這個辦法比較繞,但是可以解決完全跨域情況下的腳步置換問題。原理是利用location.hash來進行傳值。在url: http://a.com#helloword中的‘#helloworld’就是location.hash,改變hash並不會導致頁面重新整理,所以可以利用hash值來進行資料傳遞,當然資料容量是有限的。假設網域名稱a.com下的檔案cs1.html要和111cn.net網域名稱下的cs2.html傳遞資訊,cs1.html首先建立自動建立一個隱藏的iframe,iframe的src指向111cn.net網域名稱下的cs2.html頁面,這時的hash值可以做參數傳遞用。cs2.html響應請求後再將通過修改cs1.html的hash值來傳遞資料(由於兩個頁面不在同一個域下IE、Chrome不允許修改parent.location.hash的值,所以要藉助於a.com網域名稱下的一個代理iframe;Firefox可以修改)。同時在cs1.html上加一個定時器,隔一段時間來判斷location.hash的值有沒有變化,一點有變化則擷取擷取hash值。代碼如下:
先是a.com下的檔案cs1.html檔案:
| 代碼如下 |
複製代碼 |
|
function startRequest(){
var ifr = document.createElement('iframe');
ifr.style.display = 'none';
ifr.src = 'http://www.111cn.net/lab/cscript/cs2.html#paramdo';
document.body.appendChild(ifr);
}
function checkHash() {
try {
var data = location.hash ? location.hash.substring(1) : '';
if (console.log) {
console.log('Now the data is '+data);
}
} catch(e) {};
}
setInterval(checkHash, 2000); |
111cn.net網域名稱下的cs2.html:
| 代碼如下 |
複製代碼 |
|
//類比一個簡單的參數處理操作
switch(location.hash){
case '#paramdo':
callBack();
break;
case '#paramset':
//do something……
break;
}
function callBack(){
try {
parent.location.hash = 'somedata';
} catch (e) {
// ie、chrome的安全機制無法修改parent.location.hash,
// 所以要利用一個中間的cnblogs域下的代理iframe
var ifrproxy = document.createElement('iframe');
ifrproxy.style.display = 'none';
ifrproxy.src = 'http://a.com/test/cscript/cs3.html#somedata'; // 注意該檔案在"a.com"域下
document.body.appendChild(ifrproxy);
}
} |
.com下的網域名稱cs3.html
//因為parent.parent和自身屬於同一個域,所以可以改變其location.hash的值
parent.parent.location.hash = self.location.hash.substring(1);當然這樣做也存在很多缺點,諸如資料直接暴露在了url中,資料容量和類型都有限等