1. 程式人生 > >解決IE關閉頁面時連續彈出新視窗的bug

解決IE關閉頁面時連續彈出新視窗的bug

這個問題長期以來一直一個隨機出現,又十分難以重現和定位。幾經努力之後定位到了,在一個帶有複雜的內嵌iframe的頁面,當內嵌的iframe正在渲染的過程中關閉IE 瀏覽器的時候,會比較容易發生這個問題。

比較容易猜想的是IE在關閉iframe的時候出現了某些問題。猜測是幾個iframe相互有通過top的指令碼呼叫有關係,或者幾個iframe和top都在各自不同的域名下有關係,但是頁面太複雜進一步定位具體是什麼問題十分困難(當試圖簡化頁面的時候,問題往往就不能重現了)。而且問題出現在瀏覽器內部,就算具體定位到了不一定就能夠解決。所以更容易和更有意義的,可能是迴避“連續彈出新視窗”這個問題。

最開始的想法是,IE既然在出問題的時候是正在渲染iframe,那麼此時可能cpu特別繁忙導致了問題,如果可以迴避cpu的最高峰期,把渲染iframe的動作做一定的延遲,也許就可以解決問題。試驗後發現,只是減緩了頁面的展現速度,對問題的解決基本沒用幫助。

換個想法,想想如果我們自己做瀏覽器,我們在處理這個問題的時候會怎麼做呢,容易出什麼問題呢?我們可以做這樣的猜測:IE關閉視窗的時候,是先關閉和回收了最外層的window物件,然後收集它引用的各種資源,一一進行關閉和回收。也就是說,相當於一個佇列式(先開啟的window物件先銷燬回收,後開啟(被引用)的window物件後銷燬和回收)或者遞迴式(父window物件先銷燬,子window物件後銷燬)的操作:。

看看下面這個測試頁面的關閉順序,其實很耐人尋味(把程式碼儲存為一個html檔案後重新整理頁面):

<
iframe name="emu1" src="javascript:'<iframe name=emu2></iframe><iframe name=emu3></iframe>'"></iframe><iframe name="emu4" src="javascript:'<iframe name=emu5></iframe><iframe name=emu6></iframe>'"></iframe><SCRIPT LANGUAGE="JavaScript"
>
setTimeout(
function(){
    window.attachEvent('onbeforeunload',
function(){alert('parent')})
    frames['emu1'].attachEvent('onbeforeunload',
function(){alert('iframe1')})
    frames['emu1'].frames['emu2'].attachEvent('onbeforeunload',
function(){alert('iframe2')})
    frames['emu1'].frames['emu3'].attachEvent('onbeforeunload',
function(){alert('iframe3')})
    frames['emu4'].attachEvent('onbeforeunload',
function(){alert('iframe4')})
    frames['emu4'].frames['emu5'].attachEvent('onbeforeunload',
function(){alert('iframe5')})
    frames['emu4'].frames['emu6'].attachEvent('onbeforeunload',
function(){alert('iframe6')})
},
0)
</SCRIPT>

這樣子很容易帶來的一個問題是,物件的銷燬和資源的回收工作,一般來說是作為低優先順序的操作,要為高優先順序的操作讓路的,那麼在top視窗被銷燬回收的時候,各個iframe的渲染工作,作為優先順序比較高的計算,仍在繼續進行。等到各個iframe渲染完成了以後,才發現他本來以為一直在哪裡的parent(或者top)控制代碼,現在指向了一個隨機的位置,於是就發生了不可預測的後果。

假如問題真的是這樣,那麼解決方案可能是
1 對關閉操作觸發的銷燬和回收操作,提高優先級別,或者
2 一但觸發關閉操作,立刻主動停止所有的渲染操作,全部資源等待回收,或者
3 對window的關閉操作採用棧式的順序,晚建立的先銷燬,或者或者冒泡式的時序,子視窗先銷燬再銷燬父視窗。
現在我們的問題是瀏覽器自己是不會去調整自己的運算優先順序和銷燬順序的,那麼我們能否用指令碼來幫助它調整一下window物件的銷燬次序呢:

<div id="emu"><iframe src="javascript:'this is an iframe'"></iframe></div><SCRIPT LANGUAGE="JavaScript">
window.attachEvent('onbeforeunload',
function(){document.getElementById("emu").innerHTML="iframe closed";alert('parent')})
</SCRIPT>