用Ajax实现的联动列表
由于昨天把博客装饰好了,今天就来分享一下我以前做过的一个用Ajax实现的联动列表。
平常我们用服务器控件实现的多列表联动,在每次选择一项的时候都会刷新页面,如果页面元素过多,这样会使用户体验十分差。而我们用Html写死的联动列表会使一次的传输量比较大,而且可能会使一些被更新的内容不能及时展示给用户。所以才需要用Ajax来实现,Ajax可以应用在很多方面,但是不能被滥用。
前阵子有空的时候,做了一个Ajax的联动列表和Ajax的TreeView,不过后来项目任务比较多,就没有完成它们了,这个顶多是个半成品。
做这个的时候,当时的思想是不仅做好前端与后台的互动,而且要做好后台的API,让不懂前端的后台们能够轻易用上这两个东西,现在做好的仅仅是前端部分。
下面注释写得算是详细吧,有不懂的或者改进意见的可以随便说出来哦~
Ajax.js(用来和服务器交互)
var net = new Object();
net.ContentLoader = function(url, onload, onerror) {
this.url = url;
this.onload = onload;//请求完之后发生的事件
this.req = null;//XHR实例,现在初始化为空
this.onerror = (onerror) ? onerror : this.defaultError; //如果onerror存在,那么在发生错误时执行onerror,否则执行默认错误处理
this.loadXMLDoc();
}
net.ContentLoader.prototype = {
loadXMLDoc: function(url) {
if (window.XMLHttpRequest) { this.req = new XMLHttpRequest(); } //获取XHR对象
else {
try { if (window.ActiveXObject) { this.req = new ActiveXObject("Msxml2.XMLHTTP"); } }
catch (a) { this.req = new ActiveXObject("Microsoft.XMLHTTP"); }
}
},
onReadyState: function() {
var req = this.req;
var ready = req.readyState;
if (ready == 4) {//当readyState为4的时候,证明请求已经完成
var httpstatus = req.status;
if (httpstatus == 200 || httpstatus == 0)//当Status为200的时候请求正常,但是有时会出现为0的状况,没搞清楚
{ this.onload.call(this); }
else { this.onerror.call(this); }
}
},
sendAjax: function(querySting) {
//把查询字符串从URL传入
if (this.req) {
try {
//这里利用了闭包,因为onReadyState触发的时候,this总是指向Window对象,而不是我们期待的ContentLoader对象本身,
//所以用call方法和闭包,使onReadyState的上下文对象改变为ContentLoader对象本身
var loader = this;
this.req.onreadystatechange = function()
{ loader.onReadyState.call(loader); }
//运用时间戳,因为有些浏览器会缓存页面,如果请求URL相同的话,有可能会请求不到实时数据,加了时间戳可以避免这些状况
var data = new Date();
var Time = data.getTime();
this.req.open("GET", this.url + querySting + "&time=" + Time, true);
this.req.send(null);
} catch (error) {
this.onerror.call(this); //onerror方法的上下文也要是这个ContentLoader对象
}
}
},
defaultError: function() {
alert("error!" + "\n\nreadyState:"
+ this.req.readyState
+ "\nstatus:"
+ this.req.status
+ "\nherders:"
+ this.req.getAllResponseHeaders());
}
}
ListSample.js(这个的作用相当于MVC里面的Control,也是基于组件化思想开发的,前台页面使用这个联动List的时候,只要几行代码就行了)
function ListCon(masterId, slaveId,url,paramOption) {
this.slaveId = document.getElementById(slaveId); //根据从方ID,获取从方Dom对象
this.paramOption = paramOption;//保存参数对象
this.req = new net.ContentLoader(url, this.buildList); //建立ContentLoader对象,并指定回调方法为this.buildList
this.req.LC = this; //保存自己的方向连接到ContentLoader对象
if (masterId != null) {
//如果有主方ID,那么证明不是第一个List,则获取主方对象
this.masterId = document.getElementById(masterId);
this.masterId.next = this.slaveId; //主方对象保存从方对象的引用
//以下方法是用来当前面的List发生变动时来清空其后面所有后面的List
this.masterId.clearBehind = function() {
var slave = this.next;//next即为下一个的意思
slave.options.length = 0;//清空
var option = document.createElement('option');//一项都没很难看,加上默认项
option.value = 0;
option.text = "----";
slave.options.add(option);
if (slave.clearBehind) { slave.clearBehind(); } //如果slave不是最后一个,那么就会有clearBehind方法,那么就递归清空后面的
}
this.Init(); //初始化
}
else {
this.masterId = new Object(); //为了兼容有masterId的ListCon对象,这里要构造出一个Object来顶替
this.masterId.next = this.slaveId;
this.masterId.clearBehind = function() { }
this.masterChange();//自动载入第一个List的数据,这也是联动的第一个List
}
}
//构造ListCon.prototype
ListCon.prototype = {
Init: function() {
var othis = this; //这里也用到了闭包,同理是因为onchange里面的上下文this不是指向ListCon对象,所以要用这种方法来调用masterChange();
this.masterId.onchange = function() { othis.masterChange(); }
},
masterChange: function() {
var params = this.paramOption;
var sendString = "?";
if (params.type != "noparams") //如果没有参数,那么就是第一个List
{
sendString = sendString + "q=" + this.masterId.options[this.masterId.selectedIndex].value;
sendString = sendString + "&slavetable=" + this.masterId.id;
}
sendString = sendString + "&type=" + params.type;
sendString = sendString + "&table=" + params.table;
this.req.sendAjax(sendString)
},
buildList: function() {
var options = eval('(' + this.req.responseText + ')');//传回的数据是json
var optionslength = options.length;
this.LC.masterId.clearBehind();
this.LC.slaveId.options.length = 0;
if (optionslength != 0) {
var option = document.createElement('option');//创建默认项,“请选择”
option.value = 0;
option.text = "请选择";
this.LC.slaveId.options.add(option);
for (var i = 0; i < optionslength; i++)//这里由数据模型options创建视图
{
var option = document.createElement('option');
option.value = options[i].Value;
option.text = options[i].Text;
this.LC.slaveId.options.add(option);
}
}
else {
//如果返回的数据模型是空的,那么做以下处理
var option = document.createElement('option');
option.value = 0;
option.text = "----";
this.LC.slaveId.options.add(option);
}
// if (this.LC.slaveId.onchange)
// { this.LC.slaveId.onchange(); }
}
}
function creatParamsOption(table,type)
{
this.table=table;
this.type=type;
}
List.htm(前台页面)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript" src="AjaxHelp.js"></script>
<script type="text/javascript" src="ListSample.js"></script>
<script>
window.onload = function() {
new ListCon(null, "Country", " SendlistHandler.ashx", new creatParamsOption("Country", "noparams"));//因为这个是第一个list所以没有主List
new ListCon("Country", "City", "SendlistHandler.ashx", new creatParamsOption("City", "params"));
new ListCon("City", "School", "SendlistHandler.ashx", new creatParamsOption("School", "params"));
}
</script>
</head>
<body>
<select id="Country" name="D1">
<option value=0 selected="selected">请选择</option>
</select><select id="City" name="D2">
<option value=0 selected="selected">----</option>
</select><select id="School" name="D3">
<option value=0 selected="selected">----</option>
</select>
</body>
</html>
我们可以清楚的看到,组件式开发Ajax功能组件的时候,有复用性好,使用简单等特点。
这个组件仅仅据有联动功能,其实还有很多特性可以加上去,比如像前面说的,可以在后台写个配合这个的类,提供API给其他人用,以后有空再做吧~