/** jQuery version: 1.8.3 Author: 小dee Date: 2014.11.5 */
說明:分析其他網站的圖片較多,可以在目錄跳過直接看本文 demo 。
目錄:
■ 亞馬遜
■ 淘寶
■ 京東
■ 當當
■ demo1
■ demo2
■ demo3 [ 后面的博文再寫 ]
慣例,先看看他山之石,選擇了四家比較大的電商網站:亞馬遜、淘寶、京東、當當,看看它們的地址聯動菜單是怎么做的。
圖1 默認界面
說明:只能選擇第一級菜單,二三級為灰色不可用
圖2 點擊第一級菜單
說明:這種下拉方式我第一次真正注意到,平時也許用到過但是沒有察覺。我感覺這種方式體驗相對於傳統下拉菜單非常好,對於數據量不多的數據可以采用這種方式,對於數據量稍大的數據,也許能做成 ajax 翻頁的形式,不過我沒有見過。此時后兩級菜單仍然不可用。下拉數據的格式是 n 行 4 列。
圖3. 選擇一個直轄市,比如北京
說明:直轄市"北京"的第二級菜單默認自動選擇了北京,而且用戶不能修改;第三級菜單"區縣"自動展現。選擇完這一級就完成了填寫工作。
直轄市的添加用了 3 步 ( 鼠標點擊 3 次 ),當然詳細街道地址得用戶手動填寫。
下面看看普通省份。
圖4. 選擇一個普通省份
截圖省略了幾個步驟。它比選擇直轄市多了一個選擇二級地級市的步驟 ( 4步 ),其他都一樣。
圖1. 默認顯示界面
說明:固定兩級聯動菜單,第一級是選擇國內國外,第二級是選擇城市 ( 所有的省市區縣都包含在這一級的下拉菜單中 )。
圖2.點擊"請選擇城市"
說明:和亞馬遜一樣,下拉出現的是一個面板,不同的是它這個是個 tab 面板,省、市、縣包括街道這四級都包含在這里面了,默認顯示省份,所有的省份按首字首拼進行分類和排列。
圖3. 選擇一個直轄市比如北京
說明:直接顯示 tab 面板的"縣區"。
圖4.選擇"縣區",比如朝陽,顯示"街道"。加上選擇街道,用戶需要點擊 4 步。
圖5.此時如果反過來點擊"城市",顯示
圖6.如果反過來點擊"省份",則顯示"北京"高亮的面板
圖7.再選擇普通省份,比如選擇排第一行的"福建"
說明:默認跳轉至"城市"面板
淘寶的地址填寫到此結束。
相比亞馬遜,我更喜歡淘寶的設計。
總結一:這兩個網站的聯動菜單都沒有使用傳統的下拉框,實際上它們都沒有使用 html 的 <select></select>標簽,優點在於:1.不需要用戶額外再去點擊下級菜單來展開選擇項;2.不需要用戶拖動下拉菜單的豎直滾動條;3.大多數情況下用戶不用太多的視覺跨越就能找到自己需要的選項。
再來看看另兩家使用傳統選擇列表的電商網站:京東和當當。
圖1.默認界面
說明:默認三級聯動
圖2.點擊第一級下拉
圖3.選擇一個直轄市:北京
圖4. 選擇第二級菜單
圖5.
圖6. 繼續選擇
說明:直轄市選擇結束。用戶需要6步。
再看看普通省份,比如河北。圖7
圖8,選擇石家庄
圖9.選擇晉州
說明:此時動態添加了第四級菜單
圖10.繼續選擇
說明:普通省份選擇結束。用戶共需要點擊8次。
圖1.默認界面
圖3.選擇一個直轄市北京
說明:此步設計我認為是一個敗筆,當選擇第一級欄目為北京時,第二級欄目應該固定為北京或者直接顯示北京的各個區,而不是讓用戶進行一個完全多余的一個行為。
圖4.在第三級欄目中選擇北京
圖5.繼續選擇
圖5.選擇結束
說明:用戶進行了 6 步。
總結二. 使用傳統的下拉選擇菜單,用戶在選擇時可能會使用下拉的滾動條,這增加了用戶的操作;而且用戶的視覺跨度( 自上而下 ) 往往更大。站在用戶的角度,我更青睞前兩家網站的設計方式。
=======================================華麗的分割線=======================================
現在自己設計省市的多級聯動菜單,分別設計 3 種類型: 【返回目錄】【下一節:demo1】
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 });
如圖:
圖1. 3 級聯動
圖2. 4 級聯動
如需轉載,請在文章頁面保留此說明並且給出原文鏈接。謝謝!