
本文详解如何通过 jquery + ajax 实现父分类选择后动态加载对应子分类,并重点解决子选项重复出现、id 获取错误等常见问题,确保数据精准绑定与 dom 正确更新。
本文详解如何通过 jquery + ajax 实现父分类选择后动态加载对应子分类,并重点解决子选项重复出现、id 获取错误等常见问题,确保数据精准绑定与 dom 正确更新。
在构建多级分类表单(如商品类目、文章分类)时,常需实现“父分类 → 子分类”的级联下拉效果。但开发者常因 DOM 选择器误用、AJAX 响应处理不当或 PHP 输出逻辑缺陷,导致子下拉框反复追加相同选项、选项内容重复或完全无响应。以下为完整、健壮的解决方案。
✅ 核心问题定位与修复
原代码中存在两个关键错误:
-
JavaScript 端 ID 获取错误
let id = $(this).find(':selected').data('id'); // ❌ 错误:未设置 data-id 属性实际 ),应使用 .val() 获取:
let id = $('#parent_category').val(); // ✅ 推荐写法(更简洁) // 或 let id = $(this).find(':selected').val(); // ✅ 正确获取 value 值 -
PHP 端输出逻辑错误
data.php 中存在两处问题:- $output .= '...' 拼接后又 echo $output,但循环内已 echo $output .= ...,导致每行重复输出;
- 缺少 SQL 注入防护与错误处理;
- 未清空旧选项,多次触发会累积重复项(虽本例由 JS 覆盖 .html() 缓解,但逻辑应严谨)。
✅ 修正后的完整代码
index.php(前端)
<div>
<label>父分类:</label>
<select id="parent_category"><option value="">-- 请选择父分类 --</option>
<?php $select = "SELECT id, name FROM parent_category ORDER BY name";
$run = mysqli_query($con, $select);
while($row = mysqli_fetch_assoc($run)){
echo "<option value='{$row['id']}'>{$row['name']}";
}
?>
</select><br><br><label>子分类:</label>
<select id="child_category" disabled><option value="">-- 请先选择父分类 --</option></select>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script><script>
$(document).ready(function() {
$('#parent_category').on('change', function() {
const parentId = $(this).val();
// 清空并禁用子下拉框(加载中状态)
$('#child_category').empty().prop('disabled', true);
if (!parentId) {
$('#child_category').append('<option value="">-- 请先选择父分类 --');
return;
}
$.ajax({
url: 'data.php',
method: 'GET',
data: { id: parentId },
dataType: 'json', // 明确期望 JSON 响应
success: function(response) {
if (response.success && response.data.length > 0) {
$('#child_category')
.empty()
.append('<option value="">-- 请选择子分类 --')
.prop('disabled', false);
response.data.forEach(item => {
$('#child_category').append(
`<option value="${item.id}">${item.name}`
);
});
} else {
$('#child_category')
.empty()
.append('<option value="">暂无子分类')
.prop('disabled', true);
}
},
error: function() {
alert('加载子分类失败,请检查网络或服务端配置');
$('#child_category').empty().append('<option value="">加载失败');
}
});
});
});
</script>
data.php(后端)
<?php header('Content-Type: application/json; charset=utf-8');
$con = mysqli_connect("localhost", "root", "", "nilam-ghar");
if (!$con) {
die(json_encode(['success' => false, 'message' => '数据库连接失败']));
}
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
echo json_encode(['success' => false, 'data' => []]);
exit;
}
$parentId = (int)$_GET['id'];
// 使用预处理语句防止 SQL 注入(强烈推荐)
$stmt = $con->prepare("SELECT id, name FROM child_category WHERE parent_cat_id = ?");
$stmt->bind_param("i", $parentId);
$stmt->execute();
$result = $stmt->get_result();
$data = [];
while ($row = $result->fetch_assoc()) {
$data[] = $row;
}
echo json_encode(['success' => true, 'data' => $data]);
$stmt->close();
$con->close();
?>
⚠️ 关键注意事项
- 安全第一:始终对用户输入(如 $_GET['id'])进行类型校验与过滤,优先使用预处理语句(mysqli_prepare)杜绝 SQL 注入。
- 状态管理:切换父分类时,务必先 .empty() 清空子下拉框,再 .append() 新选项,避免残留或重复。
- 用户体验:添加 disabled 状态与占位提示(如“请先选择”),明确交互流程;AJAX 失败时提供友好反馈。
- 数据结构一致性:确保 child_category.parent_cat_id 字段为 INT 类型且有外键约束(可选但推荐),与 parent_category.id 类型严格匹配。
- 调试技巧:在浏览器开发者工具中检查 Network → XHR,确认请求参数、响应内容及 HTTP 状态码;在 data.php 中临时加入 error_log(print_r($_GET, true)); 辅助排查。
通过以上优化,父子下拉联动将稳定、安全、无重复地运行,为复杂表单提供可靠的数据驱动基础。











