背景
組件官網。 未用過的朋友可以先了解下。
當Content參數傳遞html元素時,官方的解釋是:
備注:1、元素不是復制而是完整移動到對話框中,所以原有的事件與屬性都將會保留 2、如果隱藏元素被傳入到對話框,會設置display:block屬性顯示該元素 3、對話框關閉的時候元素將恢復到原來在頁面的位置,style display屬性也將恢復
如果說該元素是頁面本身,最終顯示的載體也是頁面本身,那么沒有任何問題。但在使用了iframe下會有個奇怪的問題,當元素為iframe時,在chrome下,frame會重新加載一次。
問題現象
先看top.html代碼。
<iframe id="ff" src="child1.html"></iframe> <input type="button" onclick="fn()" value="彈出黃色Frame(在Chrome下frame會重新加載一次,所以值丟失了)" /> <script> function fn() { $.dialog({ title: '測試', content: document.getElementById("ff") }); } </script>
child1頁面上有個輸入框,每次加載時會顯示重新加載。我們可以在里面輸入,會發現點擊button后,IE(本機IE8)下正常,輸入的值未丟失。在Chrome下frame會被重新加載一次,導致輸入的值丟失了。
這樣帶來的問題是:init事件則有問題。init是在對話框彈出后會執行的函數,我們希望在彈出一個frame后,加載frame的數據等,而此時init函數就不能發揮作用。既然是iframe重新加載了,導致了這個問題,那么init的執行時機應該是當frame加載完畢后,再調用init事件。通過一翻搜索,找到兼容各種瀏覽器監聽iframe加載完畢的代碼:
thisFrame.onload = thisFrame.onreadystatechange = function () { if (this.readyState && this.readyState != 'complete') return; thisFrame.onload = _top.frames[config.content.name].onreadystatechange = null; //執行方法 };
於是我修改init的事件則為:
if(webkit){ //若是chrome thisFrame.onload = thisFrame.onreadystatechange = function () { if (this.readyState && this.readyState != 'complete') return; thisFrame.onload = _top.frames[config.content.name].onreadystatechange = null; //執行方法 }; } else{ //執行方法 }
到這里問題算是基本上解決了。
進一步處理
首先解釋下為什么要使用frame。
對話框需要穿越。例如主頁top.html,有個frame專門用來顯示具體的內容。某個內容頁需要彈出數據的詳情對話框,而且該對話框里面在top頂部,所以使用了artDialog提供的穿越機制,它可以直接將元素顯示top頁面的最上方。但由於artDialog的做法是將元素完整移動,所以如果是普通的div,該div又引用了很多js,穿越后會出問題,相應的事件會提示缺少js。
當然如果把這個div需要的js或css一律拷貝到top頁面上,是沒有問題的,但這樣肯定不好。所以我使用了iframe,相關的js和css都在frame內部,故不存在穿越后缺少js或者css問題。
這也引出了我自己的需求,希望在顯示穿越對話框並且元素為frame時,init事件能被順利加載。
上面的初步解決,雖然達到了目的,但是如果每個調用的地方都這么寫,會不會有點郁悶。所以我改寫了iframeTools.js文件下的through方法,做了這樣一個判斷,請看:
artDialog.through = _proxyDialog = function () { var config = arguments[0]; //檢測元素是否是frame,如果是,init的執行時機則必須保證在iframe加載完畢后,在chrome模式下,彈出frame元素會導致frame重新加載 if (config.init && config.content.tagName && config.content.tagName == 'IFRAME' && webkit) { var tmpfn = config.init; arguments[0].init = function () { var thisFrame = _top.frames[config.content.name]; thisFrame.onload = thisFrame.onreadystatechange = function () { if (this.readyState && this.readyState != 'complete') return; thisFrame.onload = _top.frames[config.content.name].onreadystatechange = null; tmpfn(); }; } } var api = _topDialog.apply(this, arguments); // 緩存從當前 window(可能為iframe)調出所有跨框架對話框, // 以便讓當前 window 卸載前去關閉這些對話框。 // 因為iframe注銷后也會從內存中刪除其創建的對象,這樣可以防止回調函數報錯 if (_top !== window) artDialog.list[api.config.id] = api; return api; };
代碼增加了一個判斷,如果through時定義了init,content是frame,並在webkit核心瀏覽器下,將init方法套一層監聽frame加載。
最后一點疑惑,為什么artDialog彈出frame元素時,在chrome下會重新加載一次。希望使用artDialog的朋友告知一下,謝謝!