jQuery实现级联下拉框实战(5)
少年张翠山 人气:0今天来完成jQuery实战的级联下拉框效果。效果功能如下:
页面默认只提供汽车厂商,当选择了具体的某品牌汽车,汽车类型下拉框就会动态的显示出来,选择对应的类型,然后出来该汽车类型对应的轮胎类型下拉框显示出来,选中轮胎类型,页面的正中间会显示出汽车的图片。
思路分析如图:
建立我们的html页面,程序清单如下:
代码清单1.1: chainSelect.jsp
<body> <div class="loading"> <p><img src="../imagehttps://img.qb5200.com/download-x/data-loading.gif" alt="数据装载中" /></p> <p>数据装载中......</p> </div> <div class="car"> <span class="carname"> 汽车厂商: <select> <option value="" selected="selected">请选择汽车厂商</option> <option value="BMW">宝马</option> <option value="Audi">奥迪</option> <option value="VW">大众</option> </select> <img src="../image/pfeil.gif" alt="有数据"> </span> <span class="cartype"> 汽车类型: <select> <option selected="selected">默认选项</option> <option>Test1</option> </select> <img alt="有数据" src="../image/pfeil.gif"> </span> <span class="wheeltype"> 车轮类型: <select> <option selected="selected">默认选项</option> <option>Test1</option> </select> </span> </div> <div class="carimage"> <p><img src="../image/img-loading.gif" alt="图片装载中" class="carloading"></p> <p><img src="" alt="汽车图片" class="carimg"></p> </div> </body>
body体里面囊括了3个div,第一个div的作用是显示“数据正在装载中…”的图片和文字。第二个div显示级联下拉效果。第三个div显示车辆图片。
css代码如下:
代码清单1.2:chainSelect.css
.loading { width: 400px; margin: 0 auto; /* visibility: hidden; */ } .loading p { text-align: center; } p { margin: 0; } .car { text-align: center; } .carimage { text-align: center; } .cartype, .wheeltype, .carloading, .carimg, .car img { display: none; }
代码清单1.3:chainSelect.js
$(document).ready(function(){ //找到三个下拉框 var carnameSelect = $(".carname").children("select"); var cartypeSelect = $(".cartype").children("select"); var wheeltypeSelect = $(".wheeltype").children("select"); carnameSelect.change(function(){ console.log("汽车厂商触发onChange事件"); }); cartypeSelect.change(function(){ console.log("汽车类型触发onChange事件"); }); wheeltypeSelect.change(function(){ console.log("车轮触发onChange事件"); }); });
首先用jQuery的class选择器选择出三个下拉的框,当它们的值改变时触发对应的jQuery函数,对jQuery函数的处理才是重点的内容。
首先说到jQuery中的ajax交互。前一篇我们用到get()的请求方式,今天来用以用post()方法的请求方式。
jQuery.post(url, [data], [callback], [type])
概述:
通过远程 HTTP POST 请求载入信息.这是一个简单的 POST 请求功
能以取代复杂ajax() 。请求成功时可调>用回调函数。如果需要在出错时执行函数,请使用 $.ajax。
参数含义:
url:发送请求地址。
data:待发送 Key/value 参数。
callback:发送成功时回调函数。
type:返回内容格式,xml, html, script, json, text, _default。
案例如下:
代码清单1.4:demo.js
$(document).ready(function(){ //发起ajax请求 $.post("../chainSelect", {name: "John", time: "2pm"}, function(data){ console.log("name : " + data.name); console.log("type : " + data.type); }, "json"); });
后台Serlvet处理如下(当然你可以使用java框架,也可以使用其他后台语言)。
代码清单1.5:demo.java
import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ChainSelect extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("name = " + request.getParameter("name")); System.out.println("time = " + request.getParameter("time")); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); String jsonStr = "{\"name\":\"fly\",\"type\":\"虫子\"}"; PrintWriter out = null; try { out = response.getWriter(); out.write(jsonStr); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { out.close(); } } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
别忘了纯Serlvet部属要在你的web.xml做配置。我的Serlvet的完整路进地址是:http://localhost:8080/JqueryStudy/chainSelect ,两句System.out.println()输出ajax传递过来的参数name和time。response.setCharacterEncoding(“UTF-8”)的作用是告诉浏览器字符串为utf-8的编码,防止中文乱码问题。response.setContentType(“application/json; charset=utf-8”)将返回的字符串以json格式形式返回。out对象是输出流,如果返回的是数组,格式应该如下:[“test1”, “test2”, “test3”],如果是json类,则格式如下:{“name”:”fly”,”type”:”虫子”}。
上诉案例返回的是json对象,后台控制台输出:
name = John
time = 2pm
前端浏览器的控制台输出:
name : fly
type : 虫子
Servlet返回数组的案例如下:
代码清单1.6:demo.java
import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ChainSelect extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("name = " + request.getParameter("name")); System.out.println("time = " + request.getParameter("time")); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); String jsonStr = "[\"test1\", \"test2\", \"test3\"]"; PrintWriter out = null; try { out = response.getWriter(); out.write(jsonStr); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { out.close(); } } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
前端jQuery代码:
$(document).ready(function(){ //发起ajax请求 $.post("../chainSelect", {name: "John", time: "2pm"}, function(data){ for(var i = 0; i < data.length; i++) { console.log((i+1) + " : " + data[i]); } }, "json"); });
后台之需要给jsonStr赋值为数组格式而已,而前端jQuery代码由于接收到的字符串数组,所以这里需要用遍历数组的形式来遍历。
本案例的Servlet代码清单:
代码清单1.7:ChainSelect.java
import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ChainSelect extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); String jsonStr = this.getStr(request.getParameter("keyword"), request.getParameter("type")); PrintWriter out = null; try { out = response.getWriter(); out.write(jsonStr); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { out.close(); } } } private String getStr(String keyword, String type) { String jsonStr = ""; if("top".equals(type)) { if("BMW".equals(keyword)) { jsonStr = "[\"316ti\", \"6ercupe\"]"; } else if("Audi".equals(keyword)) { jsonStr = "[\"tt\"]"; } else if("VW".equals(keyword)) { jsonStr = "[\"Golf4\"]"; } } else if("sub".equals(type)) { if("tt".equals(keyword)) { jsonStr = "[\"rha\", \"rhb\", \"rhc\"]"; } else if("316ti".equals(keyword)) { jsonStr = "[\"rha\", \"rhb\"]"; } else if("6ercupe".equals(keyword)) { jsonStr = "[\"rha\", \"rhb\", \"rhc\"]"; } else if("Golf4".equals(keyword)) { jsonStr = "[\"rha\", \"rhb\"]"; } } return jsonStr; } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
代码清单1.7与清单1.6的区别是,后者多了getStr()的方法,该方法用于判断前端传递过来的是一级(top)下拉框的值,还是二级(sub)下拉框的值,并根据传递的keyword返回需要的字符串。与本后台交互的是程序清单1.8所示的代码。
程序清单1.8:chainSelect.js
/** * 级联下拉框效果 */ $(document).ready(function(){ //找到三个下拉框 var carnameSelect = $(".carname").children("select"); var cartypeSelect = $(".cartype").children("select"); var wheeltypeSelect = $(".wheeltype").children("select"); carnameSelect.change(function(){ var carname = carnameSelect.val(); if(carname != "") { //汽车厂商不为空发起ajax请求 $.post("../chainSelect", {keyword: carname, type : "top"}, function(data){ if(data != null && data.length != 0) { //清除上一次change的内容 cartypeSelect.html(""); $("<option value=''>请选择汽车类型</option>").appendTo(cartypeSelect); for(var i = 0; i < data.length; i++) { $("<option value='"+data[i]+"'>"+data[i]+"</option>").appendTo(cartypeSelect); } $(".cartype").show(); carnameSelect.next("img").show(); } }, "json"); } else { //3.如果值为空,那么第二个下拉框所在span要隐藏起来,第一个下拉框后面的指示图片也要隐藏 $(".cartype").hide(); $(".wheeltype").hide(); $(".carimage").hide(); $(this).next("img").hide(); } }); cartypeSelect.change(function(){ var cartype = cartypeSelect.val(); if(cartype != "") { //汽车类型不为空发起ajax请求 $.post("../chainSelect", {keyword: cartype, type : "sub"}, function(data){ if(data != null && data.length != 0) { //清除上一次change的内容 wheeltypeSelect.html(""); $("<option value=''>请选择车轮类型</option>").appendTo(wheeltypeSelect); for(var i = 0; i < data.length; i++) { $("<option value='"+data[i]+"'>"+data[i]+"</option>").appendTo(wheeltypeSelect); } $(".wheeltype").show(); cartypeSelect.next("img").show(); } }, "json"); } else { //汽车类型为空 $(".wheeltype").hide(); $(".carimage").hide(); $(this).next("img").hide(); } }); wheeltypeSelect.change(function(){ //选中的车轮类型 var wheeltype = wheeltypeSelect.val(); if(wheeltype != "") { //选中的车辆厂商 var carname = carnameSelect.val(); //选中的车辆类型 var cartype = cartypeSelect.val(); //图片的名称 var carimgName = carname + "_" + cartype + "_" + wheeltype + ".jpg"; console.log("carimgName : " + carimgName); $(".carimage").show(); $(".carimg").attr("src", "../image/" + carimgName).load(function(){ //隐藏loading图片 $(".carloading").hide("slow"); }); $(".carimage p img").show("slow"); } else { // alert("内容为空"); // $("img").hide(); $(".carimage").hide(); } }); //给数据装载中的节点定义ajax事件,实现动画提示效果 $(".loading").ajaxStart(function(){ $(this).css("visibility", "visible"); $(this).animate({ opacity: 1 },0); }).ajaxStop(function(){ $(this).animate({ opacity: 0 },500); }); });
jQuery代码的思路是,用class选择器选择出三个下拉框,赋值给变量carnameSelect,cartypeSelect,wheeltypeSelect,默认carnameSelect下拉框是显示的,其他下拉框是隐藏。然后给他们三者注册change()事件,当用户选择下拉框的值的时候执行事件函数体里面的内容。这里我以第一级下拉框为例来讲解处理的过程。如果用户选择了第一级下拉框”汽车厂商”的”宝马”,则执行如下代码:
carnameSelect.change(function(){ var carname = carnameSelect.val(); if(carname != "") { //汽车厂商不为空发起ajax请求 $.post("../chainSelect", {keyword: carname, type : "top"}, function(data){ if(data != null && data.length != 0) { //清除上一次change的内容 cartypeSelect.html(""); $("<option value=''>请选择汽车类型</option>").appendTo(cartypeSelect); for(var i = 0; i < data.length; i++) { $("<option value='"+data[i]+"'>"+data[i]+"</option>").appendTo(cartypeSelect); } $(".cartype").show(); carnameSelect.next("img").show(); } }, "json"); } else { //3.如果值为空,那么第二个下拉框所在span要隐藏起来,第一个下拉框后面的指示图片也要隐藏 $(".cartype").hide(); $(".wheeltype").hide(); $(".carimage").hide(); $(this).next("img").hide(); } });
先将第一级下拉框内容取出来,如果值为空,那么第二个下拉框所在span要隐藏起来,第一个下拉框后面的指示图片也要隐藏。如果有内容, 则用该行代码$.post(“../chainSelect”, {keyword: carname, type : “top”}, function(data){}, “json”)向上诉的Serlvet发起post请求,post的第一个参数是Serlvet的后台地址,第二个参数画括号括起来的json数据,第三个参数是回调函数,第四个参数”json”表明发送的json数据。在回调函数中,参数data接收Serlvet返回的值,由于Serlvet返回的是可以解析为字符串数组的数据,所以用for循环来遍历得到的数据,并生成option新节点appenTo()插入到select之后。
程序清单1.8中,值得注意的地方还有$(“.loading”).ajaxStart(function(){}).ajaxStop(function(){}),这是为了美化汽车图片加载的代码。这里用到jQuery的动画专用效果的animate(),使程序淡入淡出更加的和谐。
到此几乎把级联效果实现了,但是如果在高并发环境下,每次用户切换选项都向服务器发送请求,服务器的压力可能过大。所以这里我们用jQuery的缓存来保存那些已经缓存过的请求。可以使用jQuery的data()方法。
定义和用法
从被选元素中返回附加的数据。
$(selector).data(name)
name 可选。规定要取回的数据的名称。
如果没有规定名称,则该方法将以对象的形式从元素中返回所有存储的数据。
向元素附加数据
$(selector).data(name,value)
name 必需。规定要设置的数据的名称。
value 必需。规定要设置的数据的值。
data()的使用案例如程序清单1.9:
<html> <head> <script type="text/javascript" src="/jquery/jquery.js"></script> <script type="text/javascript"> $(document).ready(function(){ $("#btn1").click(function(){ $("div").data("greeting", "Hello World"); }); $("#btn2").click(function(){ alert($("div").data("greeting")); }); }); </script> </head> <body> <button id="btn1">把数据添加到 div 元素</button><br /> <button id="btn2">获取已添加到 div 元素的数据</button> <div></div> </body> </html>
加上缓存之后的完整jQuery代码如下程序清单。
程序清单2.0:chainSelect.js
/** * 级联下拉框效果 */ $(document).ready(function(){ //找到三个下拉框 var carnameSelect = $(".carname").children("select"); var cartypeSelect = $(".cartype").children("select"); var wheeltypeSelect = $(".wheeltype").children("select"); carnameSelect.change(function(){ var carname = carnameSelect.val(); if(carname != "") { if (!carnameSelect.data(carname)) { //汽车厂商不为空发起ajax请求 $.post("../chainSelect", {keyword: carname, type : "top"}, function(data){ if(data != null && data.length != 0) { //清除上一次change的内容 cartypeSelect.html(""); $("<option value=''>请选择汽车类型</option>").appendTo(cartypeSelect); for(var i = 0; i < data.length; i++) { $("<option value='"+data[i]+"'>"+data[i]+"</option>").appendTo(cartypeSelect); } $(".cartype").show(); carnameSelect.next("img").show(); } //将data放入缓存 carnameSelect.data(carname, data); }, "json"); } else { //从缓存中取出数据 var data = carnameSelect.data(carname); if(data != null && data.length != 0) { //清除上一次change的内容 cartypeSelect.html(""); $("<option value=''>请选择汽车类型</option>").appendTo(cartypeSelect); for(var i = 0; i < data.length; i++) { $("<option value='"+data[i]+"'>"+data[i]+"</option>").appendTo(cartypeSelect); } $(".cartype").show(); carnameSelect.next("img").show(); } } } else { //3.如果值为空,那么第二个下拉框所在span要隐藏起来,第一个下拉框后面的指示图片也要隐藏 $(".cartype").hide(); $(".wheeltype").hide(); $(".carimage").hide(); $(this).next("img").hide(); } }); cartypeSelect.change(function(){ var cartype = cartypeSelect.val(); if(cartype != "") { if(!cartypeSelect.data(cartype)) { //汽车类型不为空发起ajax请求 $.post("../chainSelect", {keyword: cartype, type : "sub"}, function(data){ if(data != null && data.length != 0) { //清除上一次change的内容 wheeltypeSelect.html(""); $("<option value=''>请选择车轮类型</option>").appendTo(wheeltypeSelect); for(var i = 0; i < data.length; i++) { $("<option value='"+data[i]+"'>"+data[i]+"</option>").appendTo(wheeltypeSelect); } $(".wheeltype").show(); cartypeSelect.next("img").show(); } cartypeSelect.data(cartype, data); }, "json"); } else { var data = cartypeSelect.data(cartype); if(data != null && data.length != 0) { //清除上一次change的内容 wheeltypeSelect.html(""); $("<option value=''>请选择车轮类型</option>").appendTo(wheeltypeSelect); for(var i = 0; i < data.length; i++) { $("<option value='"+data[i]+"'>"+data[i]+"</option>").appendTo(wheeltypeSelect); } $(".wheeltype").show(); cartypeSelect.next("img").show(); } } } else { //汽车类型为空 $(".wheeltype").hide(); $(".carimage").hide(); $(this).next("img").hide(); } }); wheeltypeSelect.change(function(){ //选中的车轮类型 var wheeltype = wheeltypeSelect.val(); if(wheeltype != "") { //选中的车辆厂商 var carname = carnameSelect.val(); //选中的车辆类型 var cartype = cartypeSelect.val(); //图片的名称 var carimgName = carname + "_" + cartype + "_" + wheeltype + ".jpg"; $(".carimage").show(); //通过Javascript中的Image对象预装载图片 var cacheimg = new Image(); $(cacheimg).attr("src", "../image/" + carimgName).load(function(){ //隐藏loading图片 $(".carloading").hide("slow"); $(".carimg").attr("src", "../image/" + carimgName); }); $(".carimage p img").show("slow"); } else { $(".carimage").hide(); } }); //给数据装载中的节点定义ajax事件,实现动画提示效果 $(".loading").ajaxStart(function(){ $(this).css("visibility", "visible"); $(this).animate({ opacity: 1 },0); }).ajaxStop(function(){ $(this).animate({ opacity: 0 },500); }); });
用了data()之后,当用户选择了下拉框,并不是直接奔着服务器请求而去的,而是先判断缓存是否为空,carnameSelect.data(carname)。如果为空,则发起ajax请求,并将返回的结果放进缓存carnameSelect.data(carname, data)。如果不为空,在循环添加option节点之前data从缓存中拿到var data = carnameSelect.data(carname)。同样的,图片的缓存放进我们的Image对象中var cacheimg = new Image(),这行代码往后的第一行和第四行将缓存中的图片取出并显示出来。
加载全部内容