设为首页 收藏本站
查看: 1502|回复: 0

[经验分享] [ PHP+jQuery ] ajax 多级联动菜单的应用:电商网站的用户地址选择功能 ( 一 )

[复制链接]

尚未签到

发表于 2015-8-24 09:36:10 | 显示全部楼层 |阅读模式
/**
  jQuery version: 1.8.3
Author: 小dee
Date: 2014.11.5
*/
  说明:分析其他网站的图片较多,可以在目录跳过直接看本文 demo 。
目录:        
  ▼ 其他网站分析
   ■  亚马逊     
   ■  淘宝      
   ■  京东      
   ■  当当      
  ▼ 本文案例     
   ■ demo1   
   ■ demo2   
   ■ demo3 [ 后面的博文再写 ]
惯例,先看看他山之石,选择了四家比较大的电商网站:亚马逊、淘宝、京东、当当,看看它们的地址联动菜单是怎么做的。
1. 亚马逊【返回目录】【下一节:淘宝】
  图1 默认界面
DSC0000.jpg
  说明:只能选择第一级菜单,二三级为灰色不可用
  图2 点击第一级菜单
DSC0001.jpg
  说明:这种下拉方式我第一次真正注意到,平时也许用到过但是没有察觉。我感觉这种方式体验相对于传统下拉菜单非常好,对于数据量不多的数据可以采用这种方式,对于数据量稍大的数据,也许能做成 ajax 翻页的形式,不过我没有见过。此时后两级菜单仍然不可用。下拉数据的格式是 n 行 4 列。
  图3. 选择一个直辖市,比如北京
DSC0002.jpg
  说明:直辖市"北京"的第二级菜单默认自动选择了北京,而且用户不能修改;第三级菜单"区县"自动展现。选择完这一级就完成了填写工作。
  直辖市的添加用了 3 步 ( 鼠标点击 3 次 ),当然详细街道地址得用户手动填写。
  下面看看普通省份。
  图4. 选择一个普通省份
DSC0003.jpg
  截图省略了几个步骤。它比选择直辖市多了一个选择二级地级市的步骤 ( 4步 ),其他都一样。
  
2. 淘宝 【返回目录】【下一节:京东】
  图1. 默认显示界面
DSC0004.jpg
  说明:固定两级联动菜单,第一级是选择国内国外,第二级是选择城市 ( 所有的省市区县都包含在这一级的下拉菜单中 )。
  图2.点击"请选择城市"
   DSC0005.jpg
  
  说明:和亚马逊一样,下拉出现的是一个面板,不同的是它这个是个 tab 面板,省、市、县包括街道这四级都包含在这里面了,默认显示省份,所有的省份按首字首拼进行分类和排列。
  图3. 选择一个直辖市比如北京
DSC0006.jpg
  说明:直接显示 tab 面板的"县区"。
  图4.选择"县区",比如朝阳,显示"街道"。加上选择街道,用户需要点击 4 步。
DSC0007.jpg
  图5.此时如果反过来点击"城市",显示
DSC0008.jpg
  图6.如果反过来点击"省份",则显示"北京"高亮的面板
DSC0009.jpg
  
  图7.再选择普通省份,比如选择排第一行的"福建"
DSC00010.jpg
  
  说明:默认跳转至"城市"面板
   DSC00011.jpg
DSC00012.jpg
  淘宝的地址填写到此结束。
  相比亚马逊,我更喜欢淘宝的设计。
  总结一:这两个网站的联动菜单都没有使用传统的下拉框,实际上它们都没有使用 html 的 <select></select>标签,优点在于:1.不需要用户额外再去点击下级菜单来展开选择项;2.不需要用户拖动下拉菜单的竖直滚动条;3.大多数情况下用户不用太多的视觉跨越就能找到自己需要的选项。
  
  再来看看另两家使用传统选择列表的电商网站:京东和当当。
3.京东 【返回目录】【下一节:当当】
  图1.默认界面
   DSC00013.jpg
  
  说明:默认三级联动
  图2.点击第一级下拉
   DSC00014.jpg
  
  图3.选择一个直辖市:北京
DSC00015.jpg
  
  图4. 选择第二级菜单
DSC00016.jpg
  
  图5.
DSC00017.jpg
  
  图6. 继续选择
  
DSC00018.jpg
  说明:直辖市选择结束。用户需要6步。
  再看看普通省份,比如河北。图7
   DSC00019.jpg
  
  图8,选择石家庄
   DSC00020.jpg
  
  
  图9.选择晋州
   DSC00021.jpg
  说明:此时动态添加了第四级菜单
  
  图10.继续选择
   DSC00022.jpg
  
  说明:普通省份选择结束。用户共需要点击8次。
  
4.最后看看当当网 【返回目录】【下一节:本文案例】
  图1.默认界面
   DSC00023.jpg
  
   DSC00024.jpg
  
  图3.选择一个直辖市北京
   DSC00025.jpg
  
  说明:此步设计我认为是一个败笔,当选择第一级栏目为北京时,第二级栏目应该固定为北京或者直接显示北京的各个区,而不是让用户进行一个完全多余的一个行为。
  图4.在第三级栏目中选择北京
   DSC00026.jpg
  
  图5.继续选择
   DSC00027.jpg
  
  图5.选择结束
   DSC00028.jpg
  
  说明:用户进行了 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. 默认显示三级下拉菜单
DSC00029.jpg
  图2. 当区县含有街道信息时,动态加载第四级菜单
DSC00030.jpg
  (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 部分代码片段


DSC00031.gif DSC00032.gif


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 级联动
DSC00033.jpg
  图2. 4 级联动
DSC00034.jpg
  demo1 代码下载地址↓↓↓
  


作者:小dee

出处:http://www.iyunv.com/dee0912/

说明:作者写博目的是记录开发过程,积累经验,便于以后工作参考。
如需转载,请在文章页面保留此说明并且给出原文链接。谢谢!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-103269-1-1.html 上篇帖子: EasyEclipse for PHP首选项配置文件下载 下篇帖子: 深入理解PHP中赋值与引用
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表