首页 > 代码库 > [ PHP+jQuery ] ajax 多级联动菜单的应用:电商网站的用户地址选择功能 ( 一 ) - 传统下拉菜单
[ 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.仿淘宝的 tba 面板 [ 后面的博文写 ]
数据库分为 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(6) | 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=http://www.mamicode.com/""+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 //遍历option23 $(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 //没有选中的去掉之前的selected36 $(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=http://www.mamicode.com/""+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 //遍历option12 $(this).children().each(function(){13 14 if($(this).val() == $(this).parent().val()){15 16 //选中该条17 $(this).attr("selected",true);18 19 //把该条的id放入隐藏域20 $("#aid").val($(this).val());21 $("#a").val($(this).text());22 }else{23 24 //没有选中的去掉之前的selected25 $(this).removeAttr("selected");26 }27 28 }); 29 30 //检测是否还有下一级31 $.post("sel.php",{aid:$("#aid").val()},function(data,textStatus){32 33 //如果有返回值34 if(data){35 36 //动态添加下一级菜单37 $street = $("<select id=\"street\"><option>选择街道</option></select>");38 $street.insertAfter($("#area"));39 40 //删除之前选择省份加载的街道41 $("#street").children(":not(:first)").remove();42 43 //接收json数据44 var dataObj = eval("("+data+")"); //转换为json对象 45 46 $.each(dataObj,function(idx,item){ 47 48 $option_new = $("<option value=http://www.mamicode.com/""+item.streetID+"\">"+item.street+"</option>");49 $option_new.insertAfter($("#street").children(":first"));50 51 //如果有第四级52 if($("#street").length > 0){53 54 $("#street").live("change",function(){55 56 //遍历option57 $(this).children().each(function(){58 59 if($(this).val() == $(this).parent().val()){60 61 //选中该条62 $(this).attr("selected",true);63 64 //添加隐藏域并把把该条的id放入隐藏域65 $sid = $("<input type=\"hidden\" id=\"sid\" name=\"sid\">");66 $s = $("<input type=\"hidden\" id=\"s\" name=\"s\">");67 68 $sid.insertAfter($("#a"));69 $s.insertAfter($("#sid"));70 71 $("#sid").val($(this).val());72 $("#s").val($(this).text());73 }else{74 75 //没有选中的去掉之前的selected76 $(this).removeAttr("selected");77 }78 79 }); 80 });81 }82 })83 }else{84 85 //没有返回值,说明上一级菜单没有选中,删除第四级菜单86 $("#street").children().removeAttr("selected");87 $("#street").remove();88 }89 });90 });
提交表单:
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 级联动
demo1 下载地址
如需转载,请在文章页面保留此说明并且给出原文链接。谢谢!
[ PHP+jQuery ] ajax 多级联动菜单的应用:电商网站的用户地址选择功能 ( 一 ) - 传统下拉菜单