今天写程序的时候遇到一个问题,费了不少功夫才把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两个浏览器的机制的不同点究竟是什么?值得探讨。。。
本文记录了作者在使用AJAX加载并设置表格样式时遇到的问题及解决过程。问题表现为Firefox浏览器下表格数据加载正常但样式设置失败,而在IE浏览器下却能正常工作。最终发现原因是AJAX异步请求导致DOM元素更新滞后。

被折叠的 条评论
为什么被折叠?



