
通过 javascript 监听主单选按钮变化,动态显示对应子选项组,可轻松构建如“选择游戏主机 → 显示该主机专属关卡”的级联单选菜单。
通过 javascript 监听主单选按钮变化,动态显示对应子选项组,可轻松构建如“选择游戏主机 → 显示该主机专属关卡”的级联单选菜单。
在 Web 表单中实现“依赖型单选菜单”(即:先选一个分类,再根据所选动态展示对应的子选项),是前端交互中的经典需求。其核心逻辑是:监听主单选按钮(如游戏主机)的 change 事件,根据选中的值,仅显示匹配的子选项容器,同时隐藏其余无关项。
以下是一个完整、可直接运行的实现方案(含 HTML 结构、CSS 样式与轻量 JS 控制逻辑):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>级联单选菜单示例</title>
<style>
.course-group { display: none; margin-top: 1rem; padding-left: 1.5rem; }
.course-group.active { display: block; }
.console-label { cursor: pointer; user-select: none; }
.course-label { cursor: pointer; user-select: none; }
</style>
</head>
<body>
<form id="mainForm">
<h3>请选择游戏主机:</h3>
<div class="original-game-selector">
<input type="radio" id="SNES" name="console" value="SNES">
<label class="console-label" for="SNES">Super Nintendo (SNES)</label><br>
<input type="radio" id="N64" name="console" value="N64">
<label class="console-label" for="N64">Nintendo 64 (N64)</label><br>
<input type="radio" id="GBA" name="console" value="GBA">
<label class="console-label" for="GBA">Game Boy Advance (GBA)</label><br>
</div>
</form>
<!-- 所有子选项组统一放在表单外(或同一表单内),用 data-console 关联 -->
<div class="course-groups">
<div class="course-group" data-console="SNES">
<h4>SNES 可选关卡:</h4>
<input type="radio" id="SNES-BowserCastle3" name="course" value="Bowser Castle 3">
<label class="course-label" for="SNES-BowserCastle3">Bowser Castle 3</label><br>
<input type="radio" id="SNES-DonutPlains3" name="course" value="Donut Plains 3">
<label class="course-label" for="SNES-DonutPlains3">Donut Plains 3</label><br>
<input type="radio" id="SNES-YoshiIsland2" name="course" value="Yoshi Island 2">
<label class="course-label" for="SNES-YoshiIsland2">Yoshi Island 2</label><br>
</div>
<div class="course-group" data-console="N64">
<h4>N64 可选关卡:</h4>
<input type="radio" id="N64-BowserCastle" name="course" value="Bowser's Castle">
<label class="course-label" for="N64-BowserCastle">Bowser's Castle</label><br>
<input type="radio" id="N64-RainbowRoad" name="course" value="Rainbow Road">
<label class="course-label" for="N64-RainbowRoad">Rainbow Road</label><br>
</div>
<div class="course-group" data-console="GBA">
<h4>GBA 可选关卡:</h4>
<input type="radio" id="GBA-MushroomCup1" name="course" value="Mushroom Cup 1">
<label class="course-label" for="GBA-MushroomCup1">Mushroom Cup 1</label><br>
<input type="radio" id="GBA-FlowerCup2" name="course" value="Flower Cup 2">
<label class="course-label" for="GBA-FlowerCup2">Flower Cup 2</label><br>
</div>
</div>
<script>
// 获取所有主单选按钮和所有子选项组
const consoleRadios = document.querySelectorAll('input[name="console"]');
const courseGroups = document.querySelectorAll('.course-group');
// 为每个主单选按钮绑定 change 事件
consoleRadios.forEach(radio => {
radio.addEventListener('change', () => {
if (radio.checked) {
const selectedConsole = radio.value;
// 隐藏所有子组
courseGroups.forEach(group => group.classList.remove('active'));
// 显示匹配的子组
const targetGroup = document.querySelector(`.course-group[data-console="${selectedConsole}"]`);
if (targetGroup) {
targetGroup.classList.add('active');
}
}
});
});
// 【可选】初始状态:默认展开第一个主机(如 SNES)
// document.getElementById('SNES').checked = true;
// document.querySelector('.course-group[data-console="SNES"]').classList.add('active');
</script>
</body>
</html>✅ 关键要点说明:
-
语义清晰:使用
data-console属性将子选项组与主选项值一一绑定,避免硬编码 ID 或复杂字符串拼接; -
性能友好:仅操作 CSS 类(
.active),不修改 DOM 结构或 innerHTML,更安全、更易维护; -
无障碍友好:所有
<label></label>正确关联<input>(通过for属性),支持键盘导航与屏幕阅读器; -
可扩展性强:新增主机只需添加一个 radio + 一个
.course-group[data-console="XXX"],JS 逻辑无需改动。
⚠️ 注意事项:
- 确保所有
name="console"的单选按钮在同一表单内,否则checked状态可能异常; - 子选项的
name="course"应保持统一(本例中所有关卡共用一个字段名),便于后端统一接收; - 若需提交完整表单,建议将
.course-groups区域也包裹在<form></form>内,并确保action和method设置正确。
掌握这一模式后,你可轻松将其迁移到其他场景:如“选择国家 → 显示省份”、“选择课程类型 → 显示具体课时”等,真正实现灵活、可复用的表单交互。

















