火狐下Ajax的onreadystatechange無法調用函數的解決方法

1 、問題症狀:

var xmlHttp;
function savetodata(){
createXMLHttpRequest();
var rndcode = new Date().getTime();
var Url ="a.asp?cache="+rndcode
xmlHttp.onreadystatechange = function(){
.....

}
xmlHttp.open ("GET",Url,true );
xmlHttp.send (null);
}

上面的這段代碼, xmlHttp.onreadystatechange = function(){.....}; 可以在FF 下執行,但是如果改成

xmlHttp.open ("GET",Url,false ); 時就不行了,今天被這個問題整的暈頭轉向。

2 、原因分析:

其一: 這時不能用xmlHttp.send() ,需要內容,如果沒有內容,要用NULL

其二:經測試後發現,onreadystatechangeIE 下都很正常,但在FF3 下,只能運行readyState=0 時的代碼。不能運行readyState=4 的代碼,在網絡上找了一個原因:
ajax XMLHttpRequest.onreadystatechange 方法的差異:在 FF 中當狀態爲 1 (即 XMLHttpRequest 已經調用 open 但還沒有調用 send 時), FF 則會繼續執行onreadystatechange 後面的代碼,到執行完後面的代碼後,在執行onreadystatechange 在狀態234 的代碼,而IE 會等待狀態2 的到了,執行完onreadystatechange 中狀態234 的代碼後,繼續執行後面的代碼, 這 樣問題就出現了,經常我們在onreadystatechange 的代碼要處理從服務器上獲得的數據(這個數據只有在 onreadystatechange 的狀態爲4 時,纔可以得到),所以這在IE 中不存在問題,因爲它會等待onreadystatechange 狀態4 到來以後,在執行onreadystatechange 後面的數據,但是由於FF 不會等到onreadystatechange 狀態4 到來後在執行 onreadystatechange 後面的代碼,所以後面的代碼就不能處理從服務器上獲得的數據,那該怎麼辦呢?

解 決方法:使用javascript 的閉包(這個解決方法是從GMAP 中獲得靈感的)。我們傳遞一個函數給onreadystatechange ,在這個函 數中處理從服務器上返回的數據,但是onreadystatechange 是一個無參函數,那該怎麼辦呢?方法在我前面的Javascript attachEvent 傳遞參數的辦法已 經介紹 了,這裏再稍微介紹一下,就是傳遞一個參數給onreadystatechange ,但是在onreadystatechange 中使用return 一個 無參函數,在這個無參函數中可以使用這個傳入的參數。這個方法在IEFF 中都可以正常運行,所以這不失是一個好方法。

這裏提到採用閉包,挺複雜,另外網上有采用了在FF 下用onload ,也是不管用。經過對錯誤排除,上面摘要提到的原因,纔是根本的,也就是說,FF 下,第一次執行完onreadystatechange 後,繼續執行到send ,但後面就不會再回頭執行onreadystatechange, 一直傻傻的走下去。

我直接改成:

xmlHttp.onreadystatechange = xmlHandle;
xmlHttp.open ("GET",Url,false);
xmlHttp.send(null);
xmlHttp.onreadystatechange = xmlHandle; //
這裏加一行擋住FF ,讓它再搞一次。


function xmlHandle (){
if (xmlHttp.readyState < 4){
......
}else if (xmlHttp.readyState == 4 && xmlHttp.status == 200){
var cartResult = Number(xmlHttp.responseText);
if (cartResult == 1){
window.location.href='b.asp';
}else if (cartResult == 2){
......;
}else{
window.location.href='/';
}
}
}

但是這樣也不行,原來ff 3 改成:xmlHttp.onreadystatechange = xmlHandle(); 然而加了括號,IE 又不行,唉,原來就覺得FF 是雞皮,現在感覺FF 純屬一個打着 支持標準 的稱號,卻是幹着浪費程序員時間的垃圾。 但手上這個程序又實在重要,沒辦法,只有再調試看看有沒有更簡單的辦法,如下:

xmlHttp.open ("GET",Url,false);
xmlHttp.send (null);
if(xmlHttp.status==200)
xmlHandle();

這段代碼在IEFF 下可以通用。但由於是同步調用,需要在readyState<4 時未取得結果前出現提示,這對於網速慢的客戶很友好。然而要在本機獲得這種等待反應時的情況,由於本機反應快,會造成看不到給客戶提示,因此暫時先不用這個代碼

只有加入瀏覽器類型分析。

3 、完美解決方案

function getOs()
{   
var OsObject = "";   
if(navigator.userAgent.indexOf("MSIE")>0) {   
return "MSIE";       //IE
瀏覽器
}
if(isFirefox=navigator.userAgent.indexOf("Firefox")>0){   
return "Firefox";     //Firefox
瀏覽器
}
if(isSafari=navigator.userAgent.indexOf("Safari")>0) {   
return "Safari";      //Safan
瀏覽器
}
if(isCamino=navigator.userAgent.indexOf("Camino")>0){   
return "Camino";   //Camino
瀏覽器
}
if(isMozilla=navigator.userAgent.indexOf("Gecko/")>0){   
return "Gecko";    //Gecko
瀏覽器
}   
}

然後把AJAX 代碼改爲:

var rndcode = new Date().getTime();
var CartUrl ="a.asp?cache="+rndcode
var btype=getOs();
xmlHttp.onreadystatechange = (btype!="Firefox")?(xmlHandle):(xmlHandle());
xmlHttp.open ("GET",CartUrl,false);
xmlHttp.send(null);
xmlHttp.onreadystatechange = (btype!="Firefox")?(xmlHandle):(xmlHandle());

總算OVER 了,IE6IE 7FF 通用

4 、我的一點廢話補充

我覺得那個判斷各個瀏覽器的有些多餘,直接只判斷火狐吧,如下

var browserflag=0;

if(isFirefox=navigator.userAgent.indexOf("Firefox")>0){

browserflag=1

}

xmlHttp.onreadystatechange = (browserflag!=1)?(serverResponse):(serverResponse());

xmlHttp.open ("GET","taogogo.py",false);
xmlHttp.send(null);
xmlHttp.onreadystatechange = (browserflag!=1)?(serverResponse):(serverResponse());

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章