1. 程式人生 > >jQuery實戰5:級聯下拉框效果

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(),這行程式碼往後的第一行和第四行將快取中的圖片取出並顯示出來。