使用jQuery+HttpHandler+xml模拟一个三级联动的例子

  如下是实现过程:

  第一步:准备xml文件,并放置在网站根目录下,名为Area.xml

  

复制代码 代码如下:

  <?xml version="1.0" encoding="utf-8" ?>

  <area>

  <province id="1" name="北京">

  <city id="1" name="北京">

  <county id="1" name="东城区" />

  <county id="2" name="西城区" />

  </city>

  </province>

  <province id="2" name="河北省">

  <city id="1" name="石家庄市">

  <county id="1" name="正定县" />

  <county id="2" name="灵寿县" />

  </city>

  <city id="2" name="邯郸市">

  <county id="1" name="邯郸县" />

  <county id="2" name="永年县" />

  </city>

  </province>

  <province id="3" name="海南省">

  <city id="1" name="海口市">

  <county id="1" name="龙华区" />

  <county id="2" name="秀英区" />

  <county id="3" name="美兰区" />

  </city>

  <city id="2" name="三亚市">

  <county id="1" name="天涯镇" />

  <county id="2" name="凤凰镇" />

  </city>

  </province>

  </area>

  第二步:创建与xml文件中定义的元素对应的实体类。

  <province/>对应province类

  

复制代码 代码如下:

  public class Province

  {

  private string id;

  /// <summary>

  /// 编号

  /// </summary>

  public string Id

  {

  get { return id; }

  set { id = value; }

  }

  private string name;

  /// <summary>

  /// 名称

  /// </summary>

  public string Name

  {

  get { return name; }

  set { name = value; }

  }

  }

  <city/>对应City类:

  

复制代码 代码如下:

  public class City

  {

  private string id;

  /// <summary>

  /// 编号

  /// </summary>

  public string Id

  {

  get { return id; }

  set { id = value; }

  }

  private string name;

  /// <summary>

  /// 名称

  /// </summary>

  public string Name

  {

  get { return name; }

  set { name = value; }

  }

  }

  <county/>对应county类:

  

复制代码 代码如下:

  public class County

  {

  private string id;

  /// <summary>

  /// 编号

  /// </summary>

  public string Id

  {

  get { return id; }

  set { id = value; }

  }

  private string name;

  /// <summary>

  /// 名称

  /// </summary>

  public string Name

  {

  get { return name; }

  set { name = value; }

  }

  }

  第三步:编写服务器端处理程序类:Handler.cs

  

复制代码 代码如下:

  /// <summary>

  2 /// 处理程序

  3 /// </summary>

  4 public class Handler : IHttpHandler

  5 {

  6

  7 private static XDocument doc;

  8 private string filePath = HttpContext.Current.Server.MapPath("~/Area.xml");

  9 //javascript序列化类

  private static JavaScriptSerializer jss = new JavaScriptSerializer();

  public void ProcessRequest(HttpContext context)

  {

  context.Response.ContentType = "text/plain";

  string result = "failure";//默认返回结果为失败

  HttpRequest req = context.Request;

  string province = req["province"];//获取用户选择的省的编号

  string city = req["city"];//获取用户选择的市的编号

  string county = req["county"];//获取用户选择的县的编号

  string type = req["type"];//获取用户需要获取的省市县列表的类型

  InitDoc();

  if (type.HasValue())

  {

  switch (type.ToLower())

  {

  case "province"://如果用户需要获取省级列表

  result = jss.Serialize(GetProvinceList());

  break;

  case "city"://如果用户需要获取的是市级列表

  result = jss.Serialize(GetCityListByProvince(province));

  break;

  case "county"://如果用户需要获取的是县级列表

  result = jss.Serialize(GetCountyListByCity(province, city));

  break;

  default:

  break;

  }

  }

  //将结果以文本的格式返回给客户端

  context.Response.Write(result);

  }

  /// <summary>

  /// 初始化文档对象

  /// </summary>

  private void InitDoc()

  {

  if (doc == null)

  {

  doc = XDocument.Load(filePath);

  }

  }

  /// <summary>

  /// 初始化省级列表

  /// </summary>

  private List<Province> GetProvinceList()

  {

  List<Province> list = new List<Province>();

  if (doc != null)

  {

  XElement root = doc.Root;

  foreach (var prov in root.XPathSelectElements("province"))

  {

  list.Add(new Province()

  {

  Id = prov.Attribute("id").Value,

  Name = prov.Attribute("name").Value

  });

  }

  }

  return list;

  }

  /// <summary>

  /// 根据省级编号获取市级编号

  /// </summary>

  /// <param name="provId">省级编号</param>

  private List<City> GetCityListByProvince(string provId)

  {

  List<City> list = new List<City>();

  if (doc != null)

  {

  XElement root = doc.Root;

  //xpath表达式:/area/province[@id='1']/city

  string queryPath = "/area/province[@id='" + provId + "']/city";

  foreach (var city in root.XPathSelectElements(queryPath))

  {

  list.Add(new City()

  {

  Id = city.Attribute("id").Value,

  Name = city.Attribute("name").Value

  });

  }

  }

  return list;

  }

  /// <summary>

  /// 根据省级编号和市级编号获取县级编号

  /// </summary>

  /// <param name="provId">省级编号</param>

  /// <param name="cityId">市级编号</param>

  private List<County> GetCountyListByCity(string provId, string cityId)

  {

  List<County> list = new List<County>();

  if (doc != null)

  {

  XElement root = doc.Root;

  string queryPath = "/area/province[@id='" + provId + "']/city[@id='" + cityId + "']/county";

  foreach (var county in root.XPathSelectElements(queryPath))

  {

  list.Add(new County()

  {

  Id = county.Attribute("id").Value,

  Name = county.Attribute("name").Value

  });

  }

  }

  return list;

  }

  public bool IsReusable

  {

  get

  {

  return false;

  }

  }

  }

  在这里,查询xml我采用的是System.Xml.XPath命名空间下的XPathSelectElements(string xpath)方法和XPathSelectElement(string xpath)方法,在根据省级编号获取市级编号的方法里面,我使用了xpath表达式(假设传入的省级编号为1):/area/province[@id='1']/city,这个表达式以“/”开头,表示使用绝对路径,因为area为根节点所以从area开始,接着它下面有province元素,当我想获取area下所有province元素中id属性值为1的province元素时,我可以使用/area/province[@id='1'],即在province后面加上[@id='1']这个条件,这时我就获取到了area下id属性为1的province元素了。接着我要获取该province元素下所有的city,那么只需在后面加上/city即可,所以最终的xpath表达式为:/area/province[@id='1']/city。

  还有,因为此查询的xml是在当前网站的根目录,如果是在其它地方,那么在查询的时候要加上namespace

  将从xml文件中读取到的值组装成对应的实体对象只后,我使用了System.Web.Script.Serialization命名空间下的JavaScriptSerializer类中的Serialize方法将得到的实体对象序列化成json数据返回给客户端。

  第四步:编写html和js。

  

复制代码 代码如下:

  <html xmlns="http://www.w3.org/1999/xhtml">

  <head>

  <title>省市县三级联动下拉列表</title>

  <script src="Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>

  <script type="text/javascript">

  $(function () {

  $.post("/Handler.ashx", { "type": "province" }, function (data, status) {

  if (status == "success") {

  if (data != "failure") {

  data = $.parseJSON(data); //解析服务器返回的json数据

  for (var i = 0; i < data.length; i++) {

  var value = data[i].Id + ":" + data[i].Name; //设置option选项的值,格式为:"编号:名称"

  $("#province").append("<option value='" + value + "'>" + data[i].Name + "</option>");

  }

  }

  }

  }, "text");

  $("#province").change(function () {

  var selectValue = $(this).val(); //获取选择的省级option的值

  var provId = selectValue.split(':')[0]; //取出编号

  var provTxt = selectValue.split(':')[1]; //取出名称

  $("#txtProvince").html(provTxt); //显示选择的省的名称

  $("#city").html("<option>==请选择市==</option>"); //当省级改变时将市级清空

  $("#county").html("<option>==请选择县==</option>"); //当省级改变时将县级清空

  $.post("/Handler.ashx", { "province": provId, "type": "city" }, function (data, status) {

  if (status == "success") {

  if (data != "failure") {

  data = $.parseJSON(data);

  for (var i = 0; i < data.length; i++) {

  var value = data[i].Id + ":" + data[i].Name;

  $("#city").append("<option value='" + value + "'>" + data[i].Name + "</option>");

  }

  }

  }

  }, "text");

  });

  $("#city").change(function () {

  var provId = $("#province").val().split(':')[0];

  var selectValue = $(this).val(); //同上

  var cityId = selectValue.split(':')[0]; //同上

  var cityTxt = selectValue.split(':')[1]; //同上

  $("#txtCity").html(cityTxt); //显示选择的市的名称

  $("#county").html("<option>==请选择县==</option>"); //同上

  $.post("/Handler.ashx", { "province": provId, "city": cityId, "type": "county" }, function (data, status) {

  if (status == "success") {

  if (data != "failure") {

  data = $.parseJSON(data);

  for (var i = 0; i < data.length; i++) {

  var value = data[i].Id + ":" + data[i].Name;

  $("#county").append("<option value='" + value + "'>" + data[i].Name + "</option>");

  }

  }

  }

  }, "text");

  });

  $("#county").change(function () {

  $("#txtCounty").html($(this).val().split(':')[1]); //显示选择的县的名称

  });

  });

  </script>

  </head>

  <body>

  <!--省-->

  <select id="province" name="province">

  </select>

  <!--市-->

  <select id="city" name="city">

  </select>

  <!--县-->

  <select id="county" name="county">

  </select>

  <br />

  <span id="txtProvince" style="color: #ff0000;"></span>- <span id="txtCity" style="color: #ff0000;"></span>- <span id="txtCounty" style="color: #ff0000;"></span>

  </body>

  </html>

  关于使用jQuery与服务器通信,我使用的是$.post方法,该方法的具体使用可以参考jQuery官方文档,这里我想说的是,遍历后通过对象.属性访问时,这个属性的名字是区分大小写的,这个名字是服务器端定义的名字,因为服务器序列化的是服务器端的实体对象。

  在这个例子中,关键点就是如何使用XPath表达式,如何调用System.Xml.XPath命名空间下的XPathSelectElements(string xpath)方法。

  最终结果如下图:

使用jQuery+HttpHandler+xml模拟一个三级联动的例子

  代码13,31,50行可以优化。

  不建议多次修改DOM结构,可以拼接字符串后一次append

  数据源是xml,我会用xslt来解析xml直接输出<option>,这样就不用再前台拼接字符串了。要求所有节点ID不能有相同。

  

复制代码 代码如下:

  <select id="province" name="province" next="#city">

  </select>

  <select id="city" name="city" next="#county">

  <option>==请选择市==</option>

  </select>

  </form>

  <select id="county" name="county">

  <option>==请选择县==</option>

  </select>

  <script type="text/javascript">

  $("#province,#city").change(function () {

  var nextSelect = $(this.getAttribute("next"));

  //if (nextSelect.size() > 0) {

  nextSelect.find("option:gt(0)").remove();

  var _id = $(this).find("option:selected").val();

  var query = { parentId: _id };

  $.get("/Handler.ashx", query, function (data, status) {

  //...

  nextSelect.append("<option>...</option>....");

  });

  //}

  });

  </script>