讲师中心 微信公众号
AI工具推荐 视频效率加速

JavaScript实现动态下拉子菜单:精准控制显示与隐藏

轻磊小哥_2436

轻磊小哥_2436

发布时间:2025-11-10 11:13:01

|

968人浏览过

|

来源于php中文网

原创

JavaScript实现动态下拉子菜单:精准控制显示与隐藏

本教程旨在解决动态下拉菜单中一个常见问题:点击父级菜单项时,错误地显示所有子菜单。它将演示如何利用javascript的nextelementsibling属性,精准控制单个子菜单的可见性,确保仅显示与所点击父级项对应的子菜单,且无需大幅改动现有html结构。

引言

在现代Web应用中,下拉菜单是常见的交互元素,尤其当需要展示层级结构的内容时。一个常见的需求是实现带有子菜单的下拉菜单,即点击主菜单项后,只显示其对应的子菜单。然而,开发者在实现过程中常会遇到一个问题:无论点击哪个主菜单项,所有的子菜单都会同时显示。本教程将深入探讨这一问题,并提供一个高效且易于理解的解决方案。

问题分析

最初的实现尝试通常涉及一个JavaScript函数,该函数通过循环遍历所有预定义的子菜单ID来切换它们的显示状态。例如,如果存在subMenu1和subMenu2两个子菜单,点击任何主菜单项都会触发一个循环,导致subMenu1和subMenu2都被切换显示。

以下是导致该问题的典型JavaScript代码片段:

function subMenu() {
  for (var i = 1; i <= 2; i++) {
      document.getElementById("subMenu" + i).classList.toggle("show");
  }
}

这段代码的问题在于,它缺乏上下文信息来区分哪个主菜单项被点击,从而无法仅针对性地操作其对应的子菜单。for循环无差别地遍历并操作所有子菜单,导致了“打开所有子菜单”的 undesired 行为。

立即学习“Java免费学习笔记(深入)”;

解决方案

为了实现对单个子菜单的精准控制,我们需要在事件触发时,将当前被点击的元素作为参数传递给JavaScript函数。这样,函数就能根据这个参数定位到其对应的子菜单。

cn-json-diff
cn-json-diff

比较两个 JSON 文件并显示差异。支持嵌套结构、数组及值变更。纯 Python 标准库实现,无需 API 密钥。

下载

核心思路

解决方案的核心在于利用this关键字和DOM的nextElementSibling属性。

  1. 传递this: 在HTML的onclick事件中,将this关键字作为参数传递给subMenu函数。this在事件上下文中指向当前被点击的HTML元素。
  2. 使用nextElementSibling: 在subMenu函数内部,通过接收到的clickedElement参数,使用clickedElement.nextElementSibling属性来获取紧邻被点击元素的下一个兄弟元素。由于我们的HTML结构设计是将子菜单容器紧跟在父菜单项之后,这个兄弟元素就是我们想要操作的子菜单。

HTML 结构调整

在调用subMenu函数时,将this作为参数传入,以传递被点击的元素引用。

<div class="dropdown">
  <button onclick="mainDropDown()" class="dropbtn">主下拉菜单</button>
  <div id="myDropdown" class="dropdown-content">
    <!-- 将 this 传递给 subMenu 函数 -->
    <a href="#" onclick="subMenu(this)">A</a>
    <div class="sub-menu-item" id="subMenu1">
      <a href="#">A.1</a>
    </div>
    <a href="#" onclick="subMenu(this)">B</a>
    <div class="sub-menu-item" id="subMenu2">
      <a href="#">B.1</a>
    </div>
    <a href="#">C</a>
    <a href="#">D</a>
    <a href="#">E</a>
    <a href="#">F</a>
    <a href="#">G</a>
  </div>
</div>

CSS 样式

CSS样式主要负责定义菜单的默认隐藏状态和显示状态。.sub-menu-item类默认display: none;隐藏子菜单,而.show类则通过display: block;使其显示。

/* Dropdown Button */
.dropbtn {
  background-color: #3498DB;
  color: white;
  padding: 16px;
  font-size: 16px;
  border: none;
  cursor: pointer;
}

/* Dropdown button on hover & focus */
.dropbtn:hover, .dropbtn:focus {
  background-color: #2980B9;
}

/* The container <div> - needed to position the dropdown content */
.dropdown {
  position: relative;
  display: block;
}

/* Dropdown Content (Hidden by Default) */
.dropdown-content {
  display: none;
  position: absolute;
  background-color: #f1f1f1;
  min-width: 160px;
  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
  z-index: 1;
}

/* Links inside the dropdown */
.dropdown-content a {
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: block;
}

.sub-menu-item{
  display: none; /* 默认隐藏子菜单 */
  background-color: #ccc;
}
/* Change color of dropdown links on hover */
.dropdown-content a:hover {background-color: #ddd}

.sub-menu-item a:hover {background-color: #bbb}

/* Show the dropdown menu (use JS to add this class to the .dropdown-content container when the user clicks on the dropdown button) */
.show {display:block;}

JavaScript 逻辑优化

修改subMenu函数,使其接收一个参数(clickedElement),并利用nextElementSibling来定位并切换子菜单的可见性。

/* 当用户点击主菜单按钮时,切换显示/隐藏主下拉内容 */
function mainDropDown() {
  document.getElementById("myDropdown").classList.toggle("show");
}

/* 当用户点击子菜单项时,切换显示/隐藏对应的子菜单 */
function subMenu(clickedElement) {
  // 获取被点击元素紧邻的下一个兄弟元素
  let appropriateSubmenu = clickedElement.nextElementSibling;

  // 检查获取到的元素是否存在且是否为子菜单容器,然后切换其显示状态
  if (appropriateSubmenu && appropriateSubmenu.classList.contains('sub-menu-item')) {
      appropriateSubmenu.classList.toggle("show");
  }
}

// 点击外部区域关闭主下拉菜单(可选,但推荐的交互行为)
window.onclick = function(event) {
  if (!event.target.matches('.dropbtn')) {
    var dropdowns = document.getElementsByClassName("dropdown-content");
    for (var i = 0; i < dropdowns.length; i++) {
      var openDropdown = dropdowns[i];
      if (openDropdown.classList.contains('show')) {
        openDropdown.classList.remove('show');
      }
    }
  }
}

在subMenu函数中,我们增加了对appropriateSubmenu是否存在以及是否包含sub-menu-item类的检查,这增强了代码的健壮性,避免在DOM结构不完全符合预期时出现错误。

完整示例代码

将HTML、CSS和JavaScript整合在一起,形成一个完整可运行的示例:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>动态下拉子菜单示例</title>
<style>
/* Dropdown Button */
.dropbtn {
  background-color: #3498DB;
  color: white;
  padding: 16px;
  font-size: 16px;
  border: none;
  cursor: pointer;
}

/* Dropdown button on hover & focus */
.dropbtn:hover, .dropbtn:focus {
  background-color: #2980B9;
}

/* The container <div> - needed to position the dropdown content */
.dropdown {
  position: relative;
  display: block;
}

/* Dropdown Content (Hidden by Default) */
.dropdown-content {
  display: none;
  position: absolute;
  background-color: #f1f1f1;
  min-width: 160px;
  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
  z-index: 1;
}

/* Links inside the dropdown */
.dropdown-content a {
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: block;
}

.sub-menu-item{
  display: none; /* 默认隐藏子菜单 */
  background-color: #ccc;
}
/* Change color of dropdown links on hover */
.dropdown-content a:hover {background-color: #ddd}

.sub-menu-item a:hover {background-color: #bbb}

/* Show the dropdown menu (use JS to add this class to the .dropdown-content container when the user clicks on the dropdown button) */
.show {display:block;}
</style>
</head>
<body>

<div class="dropdown">
  <button onclick="mainDropDown()" class="dropbtn">主下拉菜单</button>
  <div id="myDropdown" class="dropdown-content">
    <a href="#" onclick="subMenu(this)">A</a>
    <div class="sub-menu-item" id="subMenu1">
      <a href="#">A.1</a>
    </div>
    <a href="#" onclick="subMenu(this)">B</a>
    <div class="sub-menu-item" id="subMenu2">
      <a href="#">B.1</a>
    </div>
    <a href="#">C</a>
    <a href="#">D</a>
    <a href="#">E</a>
    <a href="#">F</a>
    <a href="#">G</a>
  </div>
</div>

<script>
/* 当用户点击主菜单按钮时,切换显示/隐藏主下拉内容 */
function mainDropDown() {
  document.getElementById("myDropdown").classList.toggle("show");
}

/* 当用户点击子菜单项时,切换显示/隐藏对应的子菜单 */
function subMenu(clickedElement) {
  // 获取被点击元素紧邻的下一个兄弟元素
  let appropriateSubmenu = clickedElement.nextElementSibling;

  // 检查获取到的元素是否存在且是否为子菜单容器,然后切换其显示状态
  if (appropriateSubmenu && appropriateSubmenu.classList.contains('sub-menu-item')) {
      appropriateSubmenu.classList.toggle("show");
  }
}

// 点击外部区域关闭主下拉菜单
window.onclick = function(event) {
  if (!event.target.matches('.dropbtn')) {
    var dropdowns = document.getElementsByClassName("dropdown-content");
    for (var i = 0; i < dropdowns.length; i++) {
      var openDropdown = dropdowns[i];
      if (openDropdown.classList.contains('show')) {
        openDropdown.classList.remove('show');
      }
    }
  }
}
</script>

</body>
</html>

注意事项与扩展

  1. DOM 结构依赖: nextElementSibling方法高度依赖于HTML元素的紧邻兄弟关系。如果子菜单容器不是父菜单项的直接下一个兄弟元素,例如中间插入了其他
    或文本节点,则nextElementSibling将无法正确获取到目标元素。在这种情况下,可能需要使用closest()结合querySelector()或nextAll()等更灵活的DOM遍历方法。
  2. 事件委托: 对于包含大量菜单项的复杂菜单,为每个菜单项单独绑定onclick事件可能导致性能问题。更优的实践是使用事件委托

热门AI工具

更多
PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

讯飞智文

一款面向学习和办公场景的AI文档创作工具,可辅助生成PPT与Word文档,提高资料整理和内容制作效率。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

创客贴AI

创客贴AI是一款AI图像与设计工具,AI辅助的智能在线设计工具。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

相关专题

更多
DOM是什么意思
DOM是什么意思

dom的英文全称是documentobjectmodel,表示文件对象模型,是w3c组织推荐的处理可扩展置标语言的标准编程接口;dom是html文档的内存中对象表示,它提供了使用javascript与网页交互的方式。想了解更多的相关内容,可以阅读本专题下面的文章。

6541

2024.08.14

Postman身份认证怎么配置
Postman身份认证怎么配置

本专题从实际API接口调试场景出发,讲解Postman身份认证的完整配置流程,包括常见认证协议介绍、授权参数设置、访问令牌填写、OAuth 2.0授权及认证失败排查,帮助用户正确完成接口授权与访问验证。

20

2026.11.06

Postman请求头Headers怎么设置
Postman请求头Headers怎么设置

围绕Postman HTTP请求头配置展开,讲解Headers选项的具体使用方式,包括Key和Value参数填写、请求头信息添加、常见Header字段设置及请求头修改方法,帮助用户解决接口调试过程中遇到的请求头配置问题。

20

2026.11.06

Postman接口请求发送教程
Postman接口请求发送教程

详细介绍Postman接口请求的创建与发送方法,涵盖GET和POST请求、URL配置、请求参数、Headers请求头、Body请求体设置以及响应结果查看,帮助用户掌握Postman接口请求的完整操作流程。

20

2026.11.06

DBeaver怎么导入数据
DBeaver怎么导入数据

DBeaver数据导入专题,整理Excel、CSV、SQL、TXT等常见文件的数据导入方法,涵盖目标数据库选择、数据表创建、字段映射、数据类型设置及导入参数配置等操作,帮助用户将本地文件中的数据写入数据库,并了解批量导入、重复数据处理和导入失败等常见问题的解决方法。

40

2026.11.05

DBeaver安全防护教程大全
DBeaver安全防护教程大全

DBeaver安全防护专题,围绕数据库连接安全、账号密码保护和访问权限管理展开,整理SSL/TLS加密连接、SSH隧道、数据库凭据保存及证书验证等配置方法,同时介绍敏感信息保护、远程连接风险和高权限账号使用等注意事项,帮助用户提高数据库日常管理的安全性。

20

2026.11.05

DBeaver连接创建与配置教程
DBeaver连接创建与配置教程

本专题聚焦DBeaver数据库连接配置与管理,覆盖从新建连接、选择数据库类型到填写连接参数和验证连接的完整流程。针对MySQL、PostgreSQL、SQL Server等常见数据库,整理相应的连接设置方法,并提供驱动缺失、认证失败和连接异常等问题的排查方向。

40

2026.11.04

XORM框架结构体映射与表结构同步
XORM框架结构体映射与表结构同步

本专题围绕XORM框架的结构体映射展开,说明表名与字段的命名策略、xorm标签常用属性、主键自增与时间字段自动填充,并演示Sync2自动建表、增量字段升级、索引声明与反向生成结构体的完整流程。

40

2026.11.04

XORM框架安装配置与数据库连接
XORM框架安装配置与数据库连接

本专题聚焦XORM框架的配置与连接环节,拆解MySQL、PostgreSQL、SQLite、SQL Server的DSN写法与驱动选择,讲清连接池参数、多数据源拆分、读写分离与超时重连的处理方式,并补充容器与兼容数据库的适配经验。

20

2026.11.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Sass 教程
Sass 教程

共14课时 | 1.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn