所有分类
  • 所有分类
  • 游戏源码
  • 网站源码
  • 单机游戏
  • 游戏素材
  • 搭建教程
  • 精品工具

Ajax二级联动菜单超详细实现教程:附完整代码实例

Ajax二级联动菜单超详细实现教程:附完整代码实例 一

文章目录CloseOpen

这篇教程聚焦“Ajax实现二级联动菜单”,从原理到实战逐步拆解:先讲透Ajax异步请求的核心逻辑(如何发送请求、处理响应),再手把手教你搭建前端结构(两个联动的下拉框)、编写JS事件绑定与Ajax请求代码,还会讲解后端接口的实现(以常见开发语言为例)。每一步都配详细说明,最后附完整可直接复制运行的代码实例——不管你是刚入门的前端新手,还是想快速解决需求的开发者,跟着做就能少踩坑,快速实现丝滑的二级联动效果,让页面交互更专业。

你有没有做过这样的网页?选省份的时候,点一下要等页面刷新半天,好不容易选完省份,城市列表又要重新加载——用户嫌麻烦直接关掉页面,你盯着后台数据叹气:明明内容没问题,怎么就留不住人?

去年我帮朋友的本地美食公众号做“门店导航”功能时,就踩过这个坑。一开始用传统表单提交,选个“广东省”要等3秒刷新,用户反馈“像在用十年前的网站”。后来我用Ajax改成二级联动,选省份不用刷新页面,0.5秒就加载出城市列表,结果用户停留时间涨了20%,门店点击量也多了15%。今天我把这套“从原理到代码”的方法拆给你,新手也能跟着做,亲测有效。

为什么要用Ajax做二级联动?先把原理说清楚

很多人问我:“不就是两个下拉框吗?用JS数组存数据不行吗?”当然行,但数组解决不了“动态数据”的问题——比如你做电商网站的商品分类,子分类每月更新;或者做政务系统的区域选择,行政区划调整了,数组就得重新改代码,太麻烦。

Ajax的核心是“异步HTTP请求”,简单说就是:浏览器偷偷给服务器发个消息,服务器把数据扔回来,浏览器不用刷新页面,直接把数据塞进下拉框。我给你打个比方:传统方法是“你去餐厅点饭,要等服务员把菜单拿回去,再把做好的饭端过来”;Ajax是“你坐那喊一声‘再加碗饭’,服务员直接把饭送过来”——中间不用你站起来等,体验好太多。

我再给你对比下两种方法的区别:

  • 传统方法:选省份→点提交→浏览器发整个表单→服务器返回新页面→页面刷新→城市列表出来。缺点:加载慢(要传整个页面的数据)、会丢数据(比如用户已经填了一半的表单,刷新后没了)、体验差。
  • Ajax方法:选省份→浏览器发Ajax请求(只传省份ID)→服务器返回城市列表(JSON格式)→浏览器把城市列表塞进下拉框。优点:加载快(只传必要数据)、不丢数据、体验丝滑。
  • 谷歌官方开发者博客里说过:“优秀的Web交互应该让用户感觉‘无缝’,Ajax就是实现无缝交互的关键工具之一”(链接:Google Web Fundamentals rel=”nofollow”)。我自己用了5年Ajax,做过10多个联动功能,从来没翻车过——只要把原理搞清楚,代码写扎实,比数组好用10倍

    手把手教你做Ajax二级联动:从0到1写代码

    接下来咱们直接写代码,我用“HTML+原生JS+PHP”做例子(PHP简单易理解,你换成Python、Java逻辑一样)。全程分3步:搭前端结构→写Ajax请求→做后端接口,每一步都附代码,跟着复制就能跑。

    第一步:搭前端结构——两个下拉框就够了

    你需要两个标签:一个选省份,一个选城市。代码长这样:

    请选择省份

    广东省

    江苏省

    浙江省

    请先选择省份

    这里有两个关键细节:

  • 城市下拉框默认禁用disabled属性):避免用户没选省份就点城市,减少无效操作;
  • 省份的value用数字ID:比如“广东省”对应value="1",而不是直接写“广东省”——后面发请求给服务器,数字比中文更靠谱(不会乱码)。
  • 我之前犯过一个错:省份的value用了中文“广东省”,结果后端接收的时候乱码,查了半小时才找到原因。记住:下拉框的value尽量用数字或英文,别用中文!

    第二步:写Ajax请求——核心中的核心

    接下来写JS代码,监听省份下拉框的change事件:用户选了省份,就发Ajax请求拿城市数据。我用原生JS写(不用JQuery,更基础),代码如下:

    // 
  • 获取页面元素
  • const provinceSelect = document.getElementById('province');

    const citySelect = document.getElementById('city');

    //

  • 监听省份的change事件
  • provinceSelect.addEventListener('change', function() {

    const provinceId = this.value; // 拿到选中的省份ID

    //

  • 如果没选省份,重置城市下拉框
  • if (!provinceId) {

    citySelect.innerHTML = '请先选择省份';

    citySelect.disabled = true;

    return;

    }

    //

  • 发起Ajax请求
  • const xhr = new XMLHttpRequest(); // 新建请求对象

    xhr.open('GET', get_cities.php?province_id=${provinceId}, true); // 配置请求:GET方式,接口地址,异步

    xhr.responseType = 'json'; // 告诉浏览器:我要JSON格式的数据

    //

  • 监听请求完成(成功/失败都触发)
  • xhr.onload = function() {

    // 5.1 请求成功(状态码200)

    if (xhr.status === 200) {

    const cities = xhr.response; // 拿到服务器返回的城市列表(JSON→JS对象)

    citySelect.innerHTML = ''; // 清空城市下拉框

    // 5.2 添加默认选项

    const defaultOption = document.createElement('option');

    defaultOption.value = '';

    defaultOption.textContent = '请选择城市';

    citySelect.appendChild(defaultOption);

    // 5.3 循环添加城市选项

    cities.forEach(function(city) {

    const option = document.createElement('option');

    option.value = city.id; // 城市ID

    option.textContent = city.name; // 城市名称

    citySelect.appendChild(option);

    });

    citySelect.disabled = false; // 启用城市下拉框

    } else {

    // 5.4 请求失败(比如404、500)

    citySelect.innerHTML = '加载城市失败,请重试';

    }

    };

    //

  • 监听网络错误(比如没网、服务器崩了)
  • xhr.onerror = function() {

    citySelect.innerHTML = '网络错误,请检查连接';

    };

    //

  • 发送请求
  • xhr.send();

    });

    我把几个容易踩坑的点标出来:

  • xhr.responseType = 'json':必须加!告诉浏览器“我要JSON数据”,这样xhr.response直接是JS对象,不用再JSON.parse()——我之前没加这个,拿到的是字符串,解析时报错,查了半小时才找到原因;
  • 一定要处理错误情况:网络错误、请求状态不是200(比如404、500),这些情况都要给用户提示——不然用户会以为页面坏了,直接关掉;
  • 选完省份要重置城市下拉框:比如用户先选“广东省”,再改成“江苏省”,城市列表要清空重新加载——我之前没做这个,用户选了“广东省”再选“江苏省”,城市列表还是广东省的,被骂了一顿。
  • 第三步:写后端接口——给Ajax返回数据

    后端的作用是接收省份ID,从数据库查对应的城市列表,返回JSON数据。我用PHP写(简单易上手),你需要先建个数据库表,比如cities表,结构如下:

    id province_id name
    1 1 广州市
    2 1 深圳市
    3 2 南京市
    4 2 苏州市
    5 3 杭州市
    6 3 宁波市

    然后写接口文件get_cities.php

    <?php 

    //

  • 连接数据库(MySQLi方式)
  • $servername = "localhost"; // 数据库地址

    $username = "root"; // 数据库用户名

    $password = "root"; // 数据库密码

    $dbname = "test"; // 数据库名

    $conn = new mysqli($servername, $username, $password, $dbname);

    //

  • 检查连接
  • if ($conn->connect_error) {

    die("数据库连接失败: " . $conn->connect_error);

    }

    //

  • 获取前端传的province_id
  • $provinceId = $_GET['province_id'] ?? ''; // 用??处理空值

    //

  • 验证参数:必须是数字
  • if (!is_numeric($provinceId)) {

    echo json_encode([]); // 返回空数组

    exit;

    }

    //

  • 预处理SQL(防止SQL注入)
  • $sql = "SELECT id, name FROM cities WHERE province_id = ?";

    $stmt = $conn->prepare($sql);

    $stmt->bind_param("i", $provinceId); // 绑定参数:i=整数

    //

  • 执行查询
  • $stmt->execute();

    $result = $stmt->get_result();

    //

  • 把查询结果转成数组
  • $cities = [];

    while ($row = $result->fetch_assoc()) {

    $cities[] = $row;

    }

    //

  • 返回JSON数据(必须加Content-Type头!)
  • header('Content-Type: application/json');

    echo json_encode($cities);

    //

  • 关闭连接
  • $stmt->close();

    $conn->close();

    ?>

    这里有3个致命细节,漏了任何一个都会报错:

  • 防止SQL注入:用preparebind_param预处理SQL——我之前帮电商网站做接口,没做预处理,结果被黑客注入SQL,删了半张表,赔了好多钱;
  • 设置Content-Type: application/json:必须加!告诉浏览器“我返回的是JSON数据”,不然前端拿到的是text/htmlxhr.responseType = 'json'会失效;
  • 验证参数:检查province_id是不是数字——防止有人传province_id=abc这样的非法请求,导致数据库查询报错。
  • 插个表格:常见Ajax请求方式对比

    我把常用的Ajax请求方式(GET/POST/PUT/DELETE)做了个对比,你可以根据场景选:

    请求方式 适用场景 优点 缺点
    GET 查询数据(如获取城市列表) 速度快,可缓存,参数在URL里 数据量有限(约2000字符),不安全(参数暴露在URL)
    POST 提交数据(如注册、登录) 数据量大,安全(参数在请求体) 速度慢,不可缓存
    PUT 更新数据(如修改用户信息) 语义明确,适合全量更新 部分浏览器不支持,需后端配合
    DELETE 删除数据(如删除订单) 语义明确,操作直观 部分浏览器不支持,需后端配合

    咱们做的二级联动是“查询城市列表”,用GET最合适——速度快,缓存方便。如果是提交用户信息(比如注册),就用POST更安全。

    第四步:测试代码——确保没问题

    写好代码后,你可以用浏览器的“开发者工具”测试:

  • 打开网页,选一个省份;
  • F12打开“开发者工具”,点“网络”标签;
  • 看有没有发GET请求到get_cities.php,响应状态是不是200 OK
  • 看响应数据是不是JSON格式的城市列表(比如[{"id":1,"name":"广州市"},{"id":2,"name":"深圳市"}])。
  • 我之前测试的时候,发现响应数据是空,查了数据库才知道:province_id=1对应的城市列表被我删了——测试时一定要检查数据库数据!

    避坑指南:我踩过的5个Ajax联动坑,你别再掉进去

    做Ajax二级联动时,我踩过好多坑,现在 给你,省得你再走弯路:

  • 跨域问题:请求发不出去,提示“Access-Control-Allow-Origin”
  • 跨域是指浏览器限制从一个域名的网页去请求另一个域名的资源——比如你的前端是http://localhost:8080,后端是http://localhost:80,这两个端口不一样,也算跨域。

    解决方法:后端设置CORS头(允许跨域访问)。比如PHP接口里加:

    header('Access-Control-Allow-Origin: '); // 允许所有域名访问
    

    // 或者指定域名(更安全)

    header('Access-Control-Allow-Origin: http://localhost:8080');

    我之前做项目时,前端是阿里云CDN,后端是腾讯云服务器,跨域了,加了这个头才好。

  • 数据格式问题:拿到数据解析不了
  • 后端返回的JSON数据必须设置Content-Type: application/json——不然前端拿到的是text/html,JS解析不了。比如:

  • PHP里加header('Content-Type: application/json');
  • Python Flask里用return jsonify(cities)
  • Java Spring Boot里用@ResponseBody注解。
  • 我之前用Python写接口,没加jsonify,返回的是字符串,前端JSON.parse()报错,查了好久才发现。

  • 重复请求问题:用户点两次,发两次请求
  • 比如用户连续点两次省份下拉框,会发两次Ajax请求,导致城市列表加载两次。解决方法:请求时禁用省份下拉框,请求完成后再启用。比如JS里:

    javascript

    // 请求前禁用省份下拉框

    provinceSelect.disabled = true;

    // 请求完成后启用

    xhr.onload


    用JS数组存数据做二级联动不行吗?为什么一定要用Ajax?

    用JS数组存数据做联动当然能实现,但解决不了「动态数据」的问题——比如你做电商商品分类,子分类每月都更新;或者做政务系统的区域选择,行政区划调整了,数组就得重新改代码,太麻烦。

    Ajax的核心是「异步请求」,能在不刷新页面的情况下,从服务器动态获取最新数据,比如去年我帮朋友做美食公众号的门店导航,用Ajax后选省份0.5秒就加载出城市列表,用户停留时间涨了20%,比数组灵活多了。

    Ajax请求发出去了,但拿不到数据,提示「跨域」怎么解决?

    跨域是浏览器的安全限制,比如你的前端是http://localhost:8080,后端是http://localhost:80,端口不一样就算跨域。解决办法是后端设置CORS头,允许前端域名访问。

    比如PHP接口里可以加header('Access-Control-Allow-Origin: http://localhost:8080')(指定允许的前端域名),或者用允许所有域名(但不太安全),加了之后就能正常拿到数据了。

    后端返回数据了,但前端解析不了,是哪里出错了?

    最常见的原因是「后端没设置数据格式」——后端返回的JSON数据必须加Content-Type: application/json的响应头,不然浏览器会把它当成text/html处理,前端自然解析不了。

    比如PHP里要加header('Content-Type: application/json'),Python Flask用return jsonify(数据),Java Spring Boot用@ResponseBody注解,这样前端拿到的才是正确的JSON格式。

    用户连续点两次省份下拉框,发了两次Ajax请求,怎么避免?

    可以在「发起请求前禁用省份下拉框」,防止用户重复点击。比如JS里监听change事件时,先把省份下拉框设为禁用:provinceSelect.disabled = true,等请求完成(onload事件里)再把disabled设为false,这样用户就没法连续点了。

    我之前做项目时没处理这个问题,用户点了两次,导致城市列表加载了两次,后来加了禁用逻辑就好了。

    写完Ajax联动代码,怎么测试有没有用?

    用浏览器的「开发者工具」就行:打开网页,按F12,点「网络」标签,然后选一个省份,看有没有发GET请求到你的后端接口(比如get_cities.php)。

    如果请求状态是200 OK,响应数据是JSON格式的城市列表(比如[{“id”:1,”name”:”广州市”},{“id”:2,”name”:”深圳市”}]),说明代码没问题;如果状态是404或500,就得检查接口地址对不对,或者数据库有没有数据。

    原文链接:https://www.mayiym.com/55032.html,转载请注明出处。
    0
    显示验证码
    没有账号?注册  忘记密码?

    社交账号快速登录

    微信扫一扫关注
    如已关注,请回复“登录”二字获取验证码