由于最近开发的一个小项目需要用实现“Ajax三级联动下拉菜单+省市县”,我看网上有些人是直接把省市县直接放在一个JS文件的,直接用JS来调用的;还有一种是把省市县地区存在数据库中。
我选择用了“Ajax省市区三级联动+数据库”这种功能了,毕竟可以“添加、修改、删除”地区。最终的效果如下:
前提是你已经理解了:PHP + JavaScript AJAX 请求(原生)教程
一、数据库 建表
由于我用的是Mysql数据库,我就只写Mysql SQL语句了,其它的数据库按照此原理走即可!
简单说关明一下:为什么表需要有一个 level 字段?
这是因为方便好查询数据以及程序开发中所有的逻辑。如果是查询某个省下有多少市?某个市下有多少县?这个可以用 parent_id。如果想查询所有的市或所有的县?如果没有 level 字段就不好查询了。
area表
--
-- 表的结构 `area`
--
CREATE TABLE `area` (
`id` bigint(20) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`name` varchar(28) NOT NULL COMMENT '地区名称,北京',
`alias` varchar(28) NOT NULL COMMENT '地区别名,beijing',
`parent_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '地区父ID',
`level` bigint(20) NOT NULL COMMENT '层级,省=1,市=2,县=3'
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='地区表';
二、代码
这里代码包括原生:PHP代码、JavaScript代码(Ajax代码)
index.php
大家必须先懂一种思维,一般正常业务逻辑情况下,省份地区是我们第一时间打开界面的时候,就必须要先查询出来的,省份地区 这里肯定不需要ajax请求。
我这里就是先查询出所有的省级行政地区,可以用 level = 1 或 parent_id = 0 都可以。
$select_option = $sql->select($mysqli); //先查出省份的数据level = 1
foreach ($select_option as $k => $v){
$province .= "<option value='".$v['id']."'>".$v['name']."</option>";
}
require_once ABSPATH . '/view.php';
view.php
这主要是模板代码,重点看JavaScript代码部份,那里是:原生的Ajax代码。
触发Ajax我用的是onchange事件,你可以根据自己的情况来。肯定不能用Ajax轮询,每隔1秒请求一次,有点太毕浪费资源了,毕竟需要查询数据库。
由于项目要求,因此我的 select 标签 用的是 disabled=disabled,有需要隐藏的可以加上 hidden=hidden。
<div class="col-md-12">
<form class="form-horizontal" action="" method="post" enctype="multipart/form-data" autocomplete="on">
<div class="input-group mb-4">
<div class="input-group-prepend">
<span class="input-group-text" >名称</span>
<span class="input-group-text" style="color: red;background-color: white;">*</span>
</div>
<input id="name" autofocus="autofocus" type="text" required="required" name="name" maxlength="20" class="form-control" placeholder="这将是它在站点上显示的名字。">
<div class="col-md-12">
<p style="color: #757575">注意:名称是唯一的,请勿重复,否则数据库会插入数据失败!</p>
</div>
</div>
<div class="input-group mb-4">
<div class="input-group-prepend">
<span class="input-group-text" >别名</span>
<span class="input-group-text" style="color: red;background-color: white;">*</span>
</div>
<input id="alias" type="text" class="form-control" required="required" name="alias" maxlength="20" pattern="[a-z0-9_-]+" title="匹配规则未通过!" placeholder="‘别名’是在URL中使用的别称,它可以令URL更美观。通常使用小写,只能包含字母,数字,下划线(_)和连字符(-)。">
<div class="col-md-12">
<p style="color: #757575">注意:别名是唯一的,请勿重复,否则数据库会插入数据失败!</p>
</div>
</div>
<div class="input-group mb-4">
<div class="input-group-prepend">
<span class="input-group-text" >地区列表</span>
</div>
<select onchange="Loadprovince()" class="form-control" name="province" id="province">
<option value ="province">省</option>
<?php echo $province; ?>
</select>
<select onchange="Loadcity()" class="form-control" name="city" id="city" disabled="disabled">
<option value ="city">市</option>
</select>
<select class="form-control" name="county" id="county" disabled="disabled">
<option value ="county">县</option>
</select>
</div>
<div class="col-md-12">
<p style="color: #757575">一级省级行政区 包括:省、自治区、直辖市、特别行政区。</p>
<p style="color: #757575">二级市级行政区 包括:地级市、地区、自治州、盟。</p>
<p style="color: #757575">三级县级行政区 包括:市辖区、县级市、县、自治县、旗、自治旗、林区、特区。</p>
<p style="color: #757575">四级乡级行政区 包括:街道、镇、乡、民族乡、苏木、民族苏木、县辖区。</p>
</div>
<?php if($AJAX === 1){ ?>
<script>
function Loadprovince() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
if(document.getElementById("city").getAttribute("disabled")){
document.getElementById("city").removeAttribute("disabled");
}else{
//document.getElementById("city").setAttribute("hidden","hidden");
document.getElementById("county").innerHTML = '<option value="county">县</option>';
document.getElementById("county").setAttribute("disabled","disabled");
}
document.getElementById("city").innerHTML = this.responseText;
}
};
//获取省地区的ID并ajax传送到服务端,例如:四川省id=5
var data = "province=" + document.getElementById("province").value + "&type=<?php echo $type;?>";
//console.log(data);
xhttp.open("POST", "/ajax.php", true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.send(data);
}
function Loadcity() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
if(document.getElementById("county").getAttribute("disabled")){
document.getElementById("county").removeAttribute("disabled");
}
document.getElementById("county").innerHTML = this.responseText;
}
};
var data = "city=" + document.getElementById("city").value + "&type=<?php echo $type;?>";
//console.log(data);
xhttp.open("POST", "/ajax.php", true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.send(data);
}
</script>
<?php } ?>
<input type="hidden" name="token" value="<?php echo $get_token; ?>"/>
<button type="submit" class="btn btn-primary">提交</button>
<button type="reset" class="btn btn-primary">重置</button>
</form>
</div>
ajax.php
这里就是从前端接收到ajax请求后,服务端需要返回什么数据?
我这里就是直接数据库查询某个省下面有多少市的数据?某个市下面有多少县的数据?依次类推,还可以查询某个县有多少乡或镇的 数据,由于只做三级,因此就不再继续往下查询了。
由于项目加急,代码写得比较简单,先让业务先在线上跑起来再说,先保证功能正常吧!其它的后期再搞。
$ajax_province = htmlspecialchars($_POST['province'],ENT_QUOTES); //得到某个省的ID
$ajax_city = htmlspecialchars($_POST['city'],ENT_QUOTES);//得到某个市的ID
//echo $ajax_province;
if($ajax_province){
if($ajax_province === "province"){
//说明未选择任何的省,不做数据库查询
echo $city = '<option value="city">市</option>';
exit;
}else{
//数据库查询某一个省有多少市的数据pid = 省的主键id
$sql = "SELECT `id`,`name` FROM `area` WHERE `parent_id` = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("i",$ajax_province);
$stmt->execute();
$stmt->bind_result($id,$name);
$arr =[];
$arr2 =[];
while ( $stmt -> fetch ()) {
$arr['id'] = $id;
$arr['name'] = $name;
$arr2[] = $arr;
}
if($arr2){ //判断是否查出来的有数据?
$city = '<option value="city">不限</option>';
foreach ($arr2 as $k => $v){
$city .= "<option value='".$v['id']."'>".$v['name']."</option>";
}
echo $city;
exit;
}else{
echo $city = '<option value="city">不限</option>';
exit;
}
}
}elseif($ajax_city){
if($ajax_city === "city"){
//说明未选择任何的市,不做数据库查询
echo $city = '<option value="county">县</option>';
}else{
//数据库查询某一个市有多少县的数据 pid = 市的主键id
$sql = "SELECT `id`,`name` FROM `area` WHERE `parent_id` = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("i",$ajax_city);
$stmt->execute();
$stmt->bind_result($id,$name);
$arr =[];
$arr2 =[];
while ( $stmt -> fetch ()) {
$arr['id'] = $id;
$arr['name'] = $name;
$arr2[] = $arr;
}
if($arr2){ //判断是否查出来的有数据?
$city = '<option value="county">不限</option>';
foreach ($arr2 as $k => $v){
$city .= "<option value='".$v['id']."'>".$v['name']."</option>";
}
echo $city;
exit;
}else{
echo $city = '<option value="county">不限</option>';
exit;
}
}
}else{
echo '<option value="error">出错啦!</option>';
}
总结:
我在网上看了还有一种 js原生Ajax实现三级联动下拉菜单省市区代码,我个人感觉那种不是很好,因为他每次Ajax请求都会直接把所有的“省、市、区”全部查询出来;然后,后面再做一个判断是返回省,还是省+市,还是省+市+区。