jQuery實戰5:級聯下拉框效果
今天來完成jQuery實戰的級聯下拉框的效果。效果功能如下:
頁面預設只提供汽車廠商,當選擇了具體的某品牌汽車,汽車型別下拉框就會動態的顯示出來,選擇對應的型別,然後出來該汽車型別對應的輪胎型別下拉框顯示出來,選中輪胎型別,頁面的正中間會顯示出汽車的圖片。
思路分析如圖:
建立我們的html頁面,程式清單如下:
程式碼清單1.1: chainSelect.jsp
<body>
<div class="loading">
<p><img src="../image/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(),這行程式碼往後的第一行和第四行將快取中的圖片取出並顯示出來。