[ PHP+jQuery ] ajax 多級聯動菜單的應用:電商網站的用戶地址選擇功能 ( 一 ) - 傳統下拉菜單


/**
  jQuery version: 1.8.3
   Author: 小dee
   Date: 2014.11.5
*/

說明:分析其他網站的圖片較多,可以在目錄跳過直接看本文 demo

目錄:        

▼ 其他網站分析

 ■  亞馬遜     

 ■  淘寶      

 ■  京東      

 ■  當當      

▼ 本文案例     

 ■ demo1   

 ■ demo2   

 ■ demo3 [ 后面的博文再寫 ]

慣例,先看看他山之石,選擇了四家比較大的電商網站:亞馬遜、淘寶、京東、當當,看看它們的地址聯動菜單是怎么做的。

1. 亞馬遜返回目錄】【下一節:淘寶

圖1 默認界面

說明:只能選擇第一級菜單,二三級為灰色不可用

圖2 點擊第一級菜單

說明:這種下拉方式我第一次真正注意到,平時也許用到過但是沒有察覺。我感覺這種方式體驗相對於傳統下拉菜單非常好,對於數據量不多的數據可以采用這種方式,對於數據量稍大的數據,也許能做成 ajax 翻頁的形式,不過我沒有見過。此時后兩級菜單仍然不可用。下拉數據的格式是 n 行 4 列。

圖3. 選擇一個直轄市,比如北京

說明:直轄市"北京"的第二級菜單默認自動選擇了北京,而且用戶不能修改;第三級菜單"區縣"自動展現。選擇完這一級就完成了填寫工作。

直轄市的添加用了 3 步 ( 鼠標點擊 3 次 ),當然詳細街道地址得用戶手動填寫。

下面看看普通省份。

圖4. 選擇一個普通省份

截圖省略了幾個步驟。它比選擇直轄市多了一個選擇二級地級市的步驟 ( 4步 ),其他都一樣。

 

2. 淘寶 【返回目錄】【下一節:京東

圖1. 默認顯示界面

說明:固定兩級聯動菜單,第一級是選擇國內國外,第二級是選擇城市 ( 所有的省市區縣都包含在這一級的下拉菜單中 )。

圖2.點擊"請選擇城市"

 

說明:和亞馬遜一樣,下拉出現的是一個面板,不同的是它這個是個 tab 面板,省、市、縣包括街道這四級都包含在這里面了,默認顯示省份,所有的省份按首字首拼進行分類和排列。

圖3. 選擇一個直轄市比如北京

說明:直接顯示 tab 面板的"縣區"。

圖4.選擇"縣區",比如朝陽,顯示"街道"。加上選擇街道,用戶需要點擊 4 步。

圖5.此時如果反過來點擊"城市",顯示

圖6.如果反過來點擊"省份",則顯示"北京"高亮的面板

 

圖7.再選擇普通省份,比如選擇排第一行的"福建"

 

說明:默認跳轉至"城市"面板

淘寶的地址填寫到此結束。

相比亞馬遜,我更喜歡淘寶的設計。

總結一:這兩個網站的聯動菜單都沒有使用傳統的下拉框,實際上它們都沒有使用 html 的 <select></select>標簽,優點在於:1.不需要用戶額外再去點擊下級菜單來展開選擇項;2.不需要用戶拖動下拉菜單的豎直滾動條;3.大多數情況下用戶不用太多的視覺跨越就能找到自己需要的選項。

 

再來看看另兩家使用傳統選擇列表的電商網站:京東和當當。

3.京東 【返回目錄】【下一節:當當

圖1.默認界面

 

說明:默認三級聯動

圖2.點擊第一級下拉

 

圖3.選擇一個直轄市:北京

 

圖4. 選擇第二級菜單

 

圖5.

 

圖6. 繼續選擇

 

說明:直轄市選擇結束。用戶需要6步。

再看看普通省份,比如河北。圖7

 

圖8,選擇石家庄

 

 

圖9.選擇晉州

說明:此時動態添加了第四級菜單

 

圖10.繼續選擇

 

說明:普通省份選擇結束。用戶共需要點擊8次。

 

4.最后看看當當網 返回目錄】【下一節:本文案例

圖1.默認界面

 

 

圖3.選擇一個直轄市北京

 

說明:此步設計我認為是一個敗筆,當選擇第一級欄目為北京時,第二級欄目應該固定為北京或者直接顯示北京的各個區,而不是讓用戶進行一個完全多余的一個行為。

圖4.在第三級欄目中選擇北京

 

圖5.繼續選擇

 

圖5.選擇結束

 

說明:用戶進行了 6 步。

 

總結二. 使用傳統的下拉選擇菜單,用戶在選擇時可能會使用下拉的滾動條,這增加了用戶的操作;而且用戶的視覺跨度( 自上而下 ) 往往更大。站在用戶的角度,我更青睞前兩家網站的設計方式。

 

=======================================華麗的分割線=======================================

 

現在自己設計省市的多級聯動菜單,分別設計 3 種類型: 【返回目錄】【下一節:demo1

1.傳統的下拉選擇菜單

2.仿亞馬遜的下拉面板 [ 后面的博文寫 ]

3.仿淘寶的 tab 面板 [ 后面的博文寫 ]

 

數據庫分為 4 張表:province,city,area,street,分別表示省份、城市、區縣和街道,其中前三張表的數據時完整的,街道表中只有一條測試數據,用來測試動態添加級聯菜單。

表的結構如下

mysql> desc province;
+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| provinceID | varchar(6) | YES | | NULL | |
| province | varchar(40) | YES | | NULL | |
+------------+-------------+------+-----+---------+----------------+

 

mysql> desc city;
+--------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| cityID | varchar(9) | YES | | NULL | |
| city | varchar(50) | YES | | NULL | |
| father | varchar(6) | YES | | NULL | |
+--------+-------------+------+-----+---------+----------------+

 

mysql> desc area;
+--------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| areaID | varchar(50) | YES | | NULL | |
| area | varchar(60) | YES | | NULL | |
| father | varchar(6) | YES | | NULL | |
+--------+-------------+------+-----+---------+----------------+

 

mysql> desc street;
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| streetID | varchar(9) | YES | | | |
| street | varchar(40) | YES | | | |
| father | varchar(6) | YES | | | |
+----------+-------------+------+-----+---------+----------------+

 

一. 傳統的下拉菜單 返回目錄

設計思路:

默認顯示的是三級下拉菜單:省份、城市、縣區,只有選擇數據表中含有街道數據的區縣才會顯示第四級菜單,demo 完整的效果圖如下:

圖1. 默認顯示三級下拉菜單

圖2. 當區縣含有街道信息時,動態加載第四級菜單

 

(1) 首先當頁面加載時,同時使用 ajax 加載省份菜單,另外兩級菜單不可用,代碼:

addr.hrml html 部分代碼片段:

<form id="select-form" action="addr.php" method="post"> 
    
        <!-- 第一級 省份 -->
        <select id="province">    
            <option>選擇省份</option>
        </select>

        <!-- 第一級 城市 初始狀態不可用 -->
        <select id="city" disabled>    
            <option>選擇城市</option>
        </select>

        <!-- 第一級 區縣 初始狀態不可用 -->
        <select id="area" disabled>    
            <option>選擇區縣</option>
        </select>
        
        <!-- 隱藏域 -->
        <input type="hidden" id="pid" name="pid">
        <input type="hidden" id="p" name="p">
        <input type="hidden" id="cid" name="cid">
        <input type="hidden" id="c" name="c">
        <input type="hidden" id="aid" name="aid">
        <input type="hidden" id="a" name="a">

        <input id="sub" type="submit" value="提交">

    </form><br>
    
    <!-- 顯示選擇的結果 -->
    省份:<{$pid}> - <{$p}><br>
    城市:<{$cid}> - <{$c}><br>
    區縣:<{$aid}> - <{$a}><br>
    <{if $sid && $s }>
    街道:<{$sid}> - <{$s}>
    <{/if}>

 

addr.html js 部分代碼片段

$(function(){
    
        //ajax方式加載第一級菜單 - 省份
        $.post("sel.php",{
            city:true
        },function(data,textStatus){
        
            //接收json數據
            var dataObj = eval("("+data+")"); //轉換為json對象 

            $.each(dataObj,function(idx,item){ 
                                     
                $option_new = $("<option value=\""+item.provinceID+"\">"+item.province+"</option>");
                $option_new.insertAfter($("#province").children(":first"));
            })

        });

//....

 

sel.php 代碼片段:

//初始加載所有的省份信息
if($_POST['city']){

    $sql = "select provinceID,province from province order by id desc";

    if( $conne->getRowsNum($sql) > 0 ){
    
        $rows = $conne->getRowsArray($sql);
        
        //把二維數組轉換成json格式
        echo json_encode($rows);
    }
}

(2) 當選擇第一級菜單時,有兩種情況:

一是第一次選擇該菜單,需要對該級 select 標簽下的 option 進行遍歷,當select 的值 ( 選中的 ) 和 列表中 option 的值相等時,就把該option 添加上 selected 屬性,同時把該 option 的 value 和 text 放入表單的隱藏域;

二是之前已經選擇過該菜單,現在再次選擇,則需要首先把下級 select 的子元素 ( 即 option ) 已經添加的 selected 屬性去掉,同時下級 selected 全部不可用,下級菜單對應的隱藏域的值全部清空,在遍歷時需要把沒有最終選中的 option 的 selected 屬性去掉。

當選擇完省份之后,使用 ajax 加載 第二級的城市菜單,如果獲取數據成功,則加載菜單;否則城市菜單不可用。代碼如下:

addr.html js 部分代碼片段:

 1 //選擇第一級菜單 - 省份
 2 $("#province").bind("change",function(){
 3 
 4     //第三級菜單 - 地區不可用
 5     $(this).nextAll().children().removeAttr("selected");
 6     $(this).siblings("select").attr("disabled",true);
 7 
 8 
 9     //同時清除隱藏域
10     $("#cid").val("");
11     $("#c").val("");
12     $("#aid").val("");
13     $("#a").val("");
14 
15     if($("#street").length > 0){
16     
17         $("#street").remove();
18         $("#sid").val("");
19         $("#s").val("");
20     }
21     
22     //遍歷option
23     $(this).children().each(function(){
24 
25         if($(this).val() == $(this).parent().val()){
26 
27             //選中該條
28             $(this).attr("selected",true);
29             
30             //把該條的id放入隱藏域
31             $("#pid").val($(this).val());
32             $("#p").val($(this).text());
33         }else{
34         
35             //沒有選中的去掉之前的selected
36             $(this).removeAttr("selected");
37         }
38 
39     });
40 
41     //選中省份之后,使用ajax獲取第二季菜單 - 城市
42     $.post("sel.php",{
43         pid : $("#pid").val()
44     },function(data,textStatus){
45     
46         //如果有返回值
47         if(data){
48 
49             //城市菜單可用
50             $("#city").attr("disabled",false);
51 
52             //刪除之前選擇省份加載的城市
53             $("#city").children(":not(:first)").remove();
54             
55             //接收json數據
56             var dataObj = eval("("+data+")"); //轉換為json對象 
57 
58             $.each(dataObj,function(idx,item){ 
59                                      
60                 $option_new = $("<option value=\""+item.cityID+"\">"+item.city+"</option>");
61                 $option_new.insertAfter($("#city").children(":first"));
62             })
63         }else{
64         
65             //沒有返回值,說明第一級菜單沒有選中省份
66             //城市菜單恢復默認而且不可用
67             $("#city").children().removeAttr("selected");
68             $("#city").attr("disabled",true);
69         }
70     });
71 
72 });

 sel.php 代碼片段:

//當接收到post的pid,加載城市信息
if( isset($_POST['pid']) && $_POST['pid']!="選擇省份" ){

    $sql = "select cityID,city from city where father = ".$_POST['pid']." order by id desc";

    if( $conne->getRowsNum($sql) > 0 ){
    
        $rows = $conne->getRowsArray($sql);
        
        //把二維數組轉換成json格式
        echo json_encode($rows);
    }
}

其他各級的實現都與第一級的實現類似,最后是動態添加下拉菜單,如果 ajax 沒有返回查詢的數據,則說明沒有該級菜單下沒有下級菜單了,代碼如下:

addr.html js 部分代碼片段:

  1 //選擇第三級菜單 - 地區
  2 $("#area").bind("change",function(){
  3 
  4     if($("#street").length > 0){
  5     
  6         $("#street").remove();
  7         $("#sid").val("");
  8         $("#s").val("");
  9     }
 10     
 11     //遍歷option
 12     $(this).children().each(function(){
 13 
 14         if($(this).val() == $(this).parent().val()){
 15         
 16             //排除第一條(即"請選擇")
 17             if( $(this).parent().val() !== $(this).parent().children(":first").val() ){
 18 
 19                 //選中該條
 20                 $(this).attr("selected",true);
 21                 
 22                 //把該條的id和名稱放入隱藏域
 23                 $("#aid").val($(this).val());
 24                 $("#a").val($(this).text());
 25             }else{
 26             
 27                 //當是第一條時,把隱藏域的值清空,無法提交
 28                 $("#aid").val("");
 29                 $("#a").val("");
 30             }
 31         }else{
 32         
 33             //沒有選中的去掉之前的selected
 34             $(this).removeAttr("selected");
 35         }
 36 
 37     });    
 38 
 39     //檢測是否還有下一級
 40     $.post("sel.php",{aid:$("#aid").val()},function(data,textStatus){
 41     
 42         //如果有返回值
 43         if(data){
 44 
 45             //動態添加下一級菜單
 46             $street = $("<select id=\"street\"><option>選擇街道</option></select>");
 47             $street.insertAfter($("#area"));
 48 
 49             //刪除之前選擇省份加載的街道
 50             $("#street").children(":not(:first)").remove();
 51             
 52             //接收json數據
 53             var dataObj = eval("("+data+")"); //轉換為json對象 
 54 
 55             $.each(dataObj,function(idx,item){ 
 56                                      
 57                 $option_new = $("<option value=\""+item.streetID+"\">"+item.street+"</option>");
 58                 $option_new.insertAfter($("#street").children(":first"));
 59 
 60                 //如果有第四級
 61                 if($("#street").length > 0){
 62 
 63                     $("#street").live("change",function(){
 64                     
 65                         //清空隱藏域
 66                         $("#sid").remove();
 67                         $("#s").remove();
 68                         
 69                         //遍歷option
 70                         $(this).children().each(function(){
 71 
 72                             if($(this).val() == $(this).parent().val()){
 73 
 74                                 //選中該條
 75                                 $(this).attr("selected",true);
 76                                 
 77                                 //添加隱藏域並把把該條的id放入隱藏域
 78                                 $sid = $("<input type=\"hidden\" id=\"sid\" name=\"sid\">");
 79                                 $s = $("<input type=\"hidden\" id=\"s\" name=\"s\">");
 80 
 81                                 $sid.insertAfter($("#a"));
 82                                 $s.insertAfter($("#sid"));
 83 
 84                                     //排除第一條(即"請選擇")
 85                                     if( $(this).parent().val() !== $(this).parent().children(":first").val() ){
 86 
 87                                         //選中該條
 88                                         $(this).attr("selected",true);
 89                                         
 90                                         //把該條的id和名稱放入隱藏域
 91                                         $("#sid").val($(this).val());
 92                                         $("#s").val($(this).text());
 93                                     }else{
 94                                     
 95                                         //當是第一條時,把隱藏域的值清空,無法提交
 96                                         $("#sid").val("");
 97                                         $("#s").val("");
 98                                     }
 99                             }else{
100                             
101                                 //沒有選中的去掉之前的selected
102                                 $(this).removeAttr("selected");
103                             }
104 
105                         });    
106                     });
107                 }
108             })
109         }else{
110         
111             //沒有返回值,說明上一級菜單沒有選中,刪除第四級菜單
112             $("#street").children().removeAttr("selected");
113             $("#street").remove();
114         }
115     });
116 });

提交表單:

addr.html js 部分代碼片段

 1 //如果有一欄為空或者不可用,不允許提交
 2 $("#sub").click(function(){
 3 
 4     if($("#street").length == 0){ 
 5 
 6         if($("#pid").val() == "" || $("#p").val() == "" || $("#cid").val() == "" || $("#c").val() == "" || $("#aid").val() == "" || $("#a").val() == "" || $("#city").attr("disabled") === true || $("#area").attr("disabled") === true){
 7     
 8             alert("請完成選擇");
 9             return false;
10         }
11     }else{
12 
13         if($("#pid").val() == "" || $("#p").val() == "" || $("#cid").val() == "" || $("#c").val() == "" || $("#aid").val() == "" || $("#a").val() == "" || $("#city").attr("disabled") === true || $("#area").attr("disabled") === true || $("#s").length == 0 || $("#s").val() == "" || $("#sid").length == 0 || $("#sid").val() == ""){
14     
15             alert("請完成選擇");
16             return false;
17         }
18     }
19 });
View Code

如圖:

圖1. 3 級聯動

圖2. 4 級聯動

demo1 代碼下載地址↓↓↓

 

作者: 小dee
說明:作者寫博目的是記錄開發過程,積累經驗,便於以后工作參考。
如需轉載,請在文章頁面保留此說明並且給出原文鏈接。謝謝!
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM