今天写程序的时候遇到一个问题,费了不少功夫才把bug给找出来了。本来其实是很简单的问题,不过一开始的时候还是忽略了。看来低级错误还是一如既往的难以避免,特以此文记之,希望以后不会再犯同类的错误。
问题描述:
主页上定义用来显示数据的div,当页面触发了某个事件(如<select>的onchange()),就从database加载数据在该div内显示出来。而数据是用一个table来显示的,该table有css文件和javascript来设置style。
因为涉及到database的数据加载,我决定用ajax来做。于是,整个流程如下:
触发事件(select.onchange()) --> ajax (XMLHttpRequest) --> set table style(用css和javascript方法来实现)
为了方便代码的重用,把ajax那部分做成了单独的一个loadXMLDoc.js文件,并设定了一些传入参数,使得该部分代码更通用化,代码如下:
This function is to load a file by sending xmlhttprequest
When the data is being transferred, show some information to the user (usually, the info would be a loading image)
When the file loaded, display the file in the specified div
@docUrl: the file to be laoded
@docDisDiv: the div to display the file
@infoHtml: the html string, eg: "<img src='image/22-1.gif' alt='loading...'></img>";
@infoDiv: the div to display the waiting info
*/
var xmlhttp = null ;
if (window.XMLHttpRequest){ // code for Firefox, Opera, IE7, etc.
xmlhttp = new XMLHttpRequest();
} else if (window.ActiveXObject){ // code for IE6, IE5
xmlhttp = new ActiveXObject( " Microsoft.XMLHTTP " );
} if (xmlhttp != null ){
xmlhttp.onreadystatechange = function (){
if (xmlhttp.readyState == 4 ){ // 4 = "loaded"
if (xmlhttp.status == 200 ){ // 200 = "OK"
document.getElementById(docDisDiv).innerHTML = xmlhttp.responseText; // load data
document.getElementById(infoDiv).innerHTML = "" ; // data loaded, clear waiting info
} else {
alert( " Problem retrieving data: " + xmlhttp.statusText);
}
} else {
document.getElementById(infoDiv).innerHTML = infoHtml; // waiting data transfering, display waiting info...
}
}
xmlhttp.open( " GET " ,docUrl, true );
xmlhttp.send( null );
} else {
alert( " Your browser does not support XMLHTTP. " );
}
}
熟悉ajax的同志都应该清楚,这是对一个典型的“ajax文件加载请求”代码,只是里面做了作了小小的修改,传入了几个参数而已。
好了,下面再来看看op.php内的内容:
< table id = " listTable " >
< caption > The Latest Purchase Order </ caption >
< thead >
< tr class = " odd " >
< th scope = " col " abbr = " ORDER ID " > Order ID </ th >
< th scope = " col " abbr = " SELLER ID " > Seller ID </ th >
< th scope = " col " abbr = " EMPLOYEE ID " > Employee ID </ th >
< th scope = " col " abbr = " DATE " > Date </ th >
< th scope = " col " abbr = " STATUS " > Status </ th >
</ tr >
</ thead >
< tbody >
<? php
require_once ' lib/ListTableData.php ' ;
$table = " order_purchase " ;
$fields = array ( " order_id " , " seller_id " , " employee_id " , " date " , " status " );
$sql = " SELECT * FROM order_purchase ORDER BY date DESC LIMIT 20 " ;
$dataList = new ListTableData( $table , $fields , $sql );
$dataList -> printData();
?>
</ tbody >
</ table >
</ div >
关于这个文件的代码,其实也不用理会,只要明白,这个table有个id属性,然后外部代码根据这个id就可以将这个table的style按照css和js定义好风格来设置。
最后,我们再来看看在index.php中代码是怎么被调用的:
var docDiv = " right_top_itemlist " ;
var infoHtml = " <img src='image/22-1.gif' alt='loading...'></img> " ;
var infoDiv = " loading_info_div " ;
loadXMLDoc(docUrl,docDiv,infoHtml,infoDiv);//load file and construct table
loadXMLDoc(docUrl,docDiv,infoHtml,infoDiv); 这一行就没什么说的了,就是调用那个js文件的方法。
setTableStyle(); 这一行目的就是将加载后的table的style设置好,至于当中的style代码跟这个主题无关紧要,因此不用理会,知道它的作用就行了。
简单概括一下就是:发送ajax请求,文件加载的时候显示等候信息,当文件加载完就以table的形式显示数据,然后将table的style设置好。
好了,开始测试。测试结果:在IE下,成功得到了设想当中的效果,可是在Firefox下,只能成功加载数据,但表格的style设置不成功。
于是用firefox的debug工具,在其中一个js语句内:
出现了如下的错误信息:error document.getElementById(id) has no properties(其中的id就是我从外面传进去的一个table的名字,在这里也就是“listTable”了) 。
我一直都很纳闷,不知道为什么会出现这个error,因为我通过alert()方法测试,确定“listTable”已经正确传进去了,但是为什么却又出现了这个错误呢?代码改来改去,不断的测试,可还是百思不得其解。。。
后来,我在设置table的那段代码中加入了一个语句来测试所有"tr”标签的总数量,结果发现,每次alert出来的<tr>标签的数量竟然不是当前加载的table的<tr>的数量,而是上一次加载的table的那个数量,也就是说,这段"setTableStyle" 代码set的根本就不是当前显示数据的那个table,而是上一次加载数据的那个table。(在这里要补充一下:由于加载的那些数据是动态的,因此每次加载的行数,也就是<tr>的数量都不同的)那它的style是当然不可能被正确设置了。可是,为什么会出现这样的问题呢?明明就是在loadXMLDoc()之后setTableStyle()了啊。。。并且,在IE下面也是成功通过的啊。。。到底是哪里出的问题呢?想啊想,debug啊debug。。。几乎抓破头皮了,还是没找出来原因。。。
后来,突然脑海好像闪了一下的样子。。。我想到了些东西。。。
对!问题就在这里,一定是这里:if (xmlhttp.readyState == 4 ){ // 4 = "loaded"
if (xmlhttp.status == 200 ){ // 200 = "OK"
...
当xmlhttp请求发送出去以后,先从database提取数据,然后数据返回来,然后才去构造那个"listTable”,而并不是马上就将"listTable”构建的,是需要一定时间的,这也是为什么我们很多时候将loading image显示出来的原因!显然,当loadXMLDoc()被执行之后,紧跟着就执行setTableStyle(),从表面上看执行顺序是没错的,然而实际上是,当setTableStyle()开始运行时,xmlhttp还在请求当中,数据还没完全返回来,"listTable” 根本还没有构建起来。。。这也就是为什么,后来我alert()得到的<tr>数目并不是当前显示的table的tr数目,而是上一个table的tr数目--因为这时候,新的table根本还没构建。。。也就是说,当新的table构建好以后,根本就没有对当前的table进行setTableStyle(),而是对上个table做了。
后来将setTableStyle()的调用放在了xmlhttp的请求完成后,也就是:
if (xmlhttp.readyState == 4 ){ // 4 = "loaded"
if (xmlhttp.status == 200 ){ // 200 = "OK"
document.getElementById(docDisDiv).innerHTML = xmlhttp.responseText; // load data
document.getElementById(infoDiv).innerHTML = "" ; // data loaded, clear waiting info
setTableStyle();
} else {
alert( " Problem retrieving data: " + xmlhttp.statusText);
}
终于,成功了。再debug就没有错了,无论是在ie下还是ff下。。。
其实说了一大堆也就是一言可以蔽之:在使用ajax xmlhttprequest的时候要注意,方法的调用时机要合适,不能想当然。
正如我在文章开头说到的,本来是一个很基本的问题,但是因为没有注意好,结果弄了大半天才高明白,bs一下自己。。。
不过,这里又提出了一个问题,不知道为什么,在ie下面,没有出现这个问题的呢???这个问题所反映出的ie和ff两个浏览器的机制的不同点究竟是什么?值得探讨。。。