0

0

构建动态且独立的多个下拉菜单:HTML、CSS与JavaScript实现教程

DDD

DDD

发布时间:2025-10-22 11:43:44

|

252人浏览过

|

来源于php中文网

原创

构建动态且独立的多个下拉菜单:HTML、CSS与JavaScript实现教程

本教程旨在解决网页中实现多个独立下拉菜单时遇到的常见问题,特别是由于id重复和事件处理不当导致的下拉内容无法正确显示在其对应按钮下方。我们将通过优化html结构、采用现代javascript事件监听机制,并精确控制事件传播,确保每个下拉菜单都能独立运作,并在点击页面其他区域时自动关闭,从而提供一个高效、可维护的解决方案。

在现代网页设计中,下拉菜单(Dropdown Menu)是一种常见的交互元素,用于节省空间并组织内容。然而,当页面上存在多个独立的下拉菜单时,如果不正确地处理它们的行为,可能会遇到一些挑战,例如所有下拉菜单同时显示、点击某个按钮却激活了错误的下拉菜单,或者下拉菜单无法在点击页面其他区域时自动关闭。本教程将详细介绍如何通过优化HTML、CSS和JavaScript来构建功能完善、独立运作的多个下拉菜单。

理解常见问题与根源

许多开发者在实现多个下拉菜单时,可能会遇到以下问题:

  1. 下拉内容无法在其对应按钮下方显示:这通常是由于CSS定位问题,或者JavaScript逻辑未能正确识别并操作与被点击按钮对应的下拉内容。
  2. 所有下拉菜单同时显示或只显示第一个:这往往是由于在HTML中使用了重复的id属性,或者JavaScript在尝试获取元素时,document.getElementById()总是返回第一个匹配的元素。HTML中的id属性必须是唯一的。
  3. 下拉菜单点击后立即关闭:这可能是因为按钮点击事件与全局的关闭事件(例如点击window)同时触发,导致下拉菜单在打开后又被立即关闭。

核心实现原理与最佳实践

为了解决上述问题,我们将遵循以下核心原则:

  1. 唯一标识与关联:避免使用重复的id。通过遍历元素集合,并利用其在DOM中的相对位置(例如父子关系或索引)来关联按钮和其对应的下拉内容。
  2. 现代事件处理:放弃使用内联事件处理器(如onclick),转而使用addEventListener方法。这有助于分离HTML结构与JavaScript行为,提高代码的可维护性和可读性。
  3. 精确的DOM操作:当按钮被点击时,精确地找到其对应的下拉内容并切换其显示状态。
  4. 管理下拉菜单状态:实现逻辑以确保在打开一个下拉菜单时,其他已打开的下拉菜单能够自动关闭。
  5. 控制事件传播:利用event.stopPropagation()来阻止事件冒泡,防止按钮点击事件意外触发全局的关闭事件。

HTML结构

首先,我们来定义每个下拉菜单的HTML结构。每个下拉菜单都应该包含一个触发按钮和一个包含链接的下拉内容区域。为了更好地组织,我们将按钮和内容包裹在一个共同的父容器.dropdown中。

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

注意:

  • 移除了myFunction()内联事件处理器。
  • 移除了id="myDropdown",因为每个下拉内容现在都将通过其父级或索引来引用。

CSS样式

CSS主要负责下拉菜单的布局和显示隐藏。关键在于将.dropdown容器设置为position: relative,而.dropdown-content设置为position: absolute,这样下拉内容就能相对于其父容器定位,并显示在其下方。

nav {
  display: flex; /* 使用Flexbox布局导航项 */
}

.dropbtn {
  font-size: 12px;    
  border: none;
  outline: none;
  text-align: center;
  color: #f2f2f2;
  width: 155px;
  padding: 14px 16px;
  background-color: inherit;
  font-family: serif;
  text-transform: capitalize;
  border-right: 1px solid gray;
  border-bottom: 1px solid gray;
  cursor: pointer; /* 提示用户这是一个可点击元素 */
}

.dropbtn:hover, .dropbtn:focus {
  background-color: #2980B9;
}

.dropdown {
  position: relative; /* 关键:使下拉内容相对于此容器定位 */
}

.dropdown-content {
  display: none; /* 默认隐藏 */
  position: absolute; /* 关键:相对于父级`.dropdown`定位 */
  background-color: #f1f1f1;
  min-width: 200px;
  height: 200px;
  overflow: hidden;
  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
  z-index: 1; /* 确保下拉内容显示在其他元素之上 */
  border-radius: 10px;
  padding-bottom: 20px;
  padding-top: 40px;
  opacity: .9;
}

.dropdown-content a {
  color: blue;
  padding: 12px 20px;
  text-decoration: none;
  display: block;
  width: 100%;
  font-size: 12px;
  background-color: transparent; /* 修复了原代码中的空值 */
  border-bottom: 1px solid gray;
}

.dropdown a:hover {
  background-color: gray;
}

.show {
  display: block; /* 用于显示下拉内容 */
}

JavaScript逻辑

JavaScript是实现下拉菜单交互的核心。我们将使用addEventListener来监听按钮点击事件和全局的窗口点击事件,并精确控制每个下拉菜单的显示与隐藏。

// 获取所有下拉内容和下拉按钮,并转换为数组以便使用forEach
const dropdowns = Array.from(document.getElementsByClassName("dropdown-content"));
const dropdownButtons = Array.from(document.getElementsByClassName('dropbtn'));

let currentDropdownIndex = -1; // 记录当前打开的下拉菜单的索引
let openDropdownCount = 0;     // 记录当前打开的下拉菜单数量

// 为每个下拉按钮添加点击事件监听器
dropdownButtons.forEach(function(dropdownBtn, index) {
  dropdownBtn.addEventListener('click', function(e) {
    e.stopPropagation(); // 阻止事件冒泡,防止触发window的点击事件

    // 切换当前点击按钮对应下拉菜单的显示状态
    dropdowns[index].classList.toggle('show');

    // 更新当前打开的下拉菜单索引
    currentDropdownIndex = index;

    // 检查是否有其他下拉菜单是打开状态,如果是,则关闭它们
    for (let i = 0; i < dropdowns.length; i++) {
      const openDropdown = dropdowns[i];
      // 如果某个下拉菜单是打开的,并且不是当前点击的这个,就关闭它
      if (openDropdown.classList.contains('show') && i !== currentDropdownIndex) {
        openDropdown.classList.remove('show');
      }
    }

    // 重新计算打开的下拉菜单数量(确保只有一个或零个)
    openDropdownCount = dropdowns[index].classList.contains('show') ? 1 : 0;
  });
});

// 为window添加点击事件监听器,用于点击页面其他区域时关闭所有下拉菜单
window.addEventListener('click', function(event) {
  // 遍历所有下拉菜单,如果它们是打开的,就关闭它们
  for (let i = 0; i < dropdowns.length; i++) {
    const openDropdown = dropdowns[i];
    if (openDropdown.classList.contains('show')) {
      openDropdown.classList.remove('show');
    }
  }
  openDropdownCount = 0; // 重置打开的下拉菜单计数
  currentDropdownIndex = -1; // 重置当前打开的下拉菜单索引
});

JavaScript代码详解:

ima.copilot
ima.copilot

腾讯大混元模型推出的智能工作台产品,提供知识库管理、AI问答、智能写作等功能

下载
  1. 获取元素并转换为数组

    • document.getElementsByClassName()返回的是一个HTMLCollection,它不是一个真正的数组,因此我们使用Array.from()将其转换为数组,以便能够使用forEach等数组方法。
    • dropdowns存储所有下拉内容元素,dropdownButtons存储所有下拉按钮元素。
  2. 按钮点击事件处理

    • dropdownButtons.forEach():为每个下拉按钮添加一个点击事件监听器。
    • e.stopPropagation():这是非常关键的一步。当点击一个dropbtn时,事件会从dropbtn开始向上冒泡到window。如果没有stopPropagation(),window上的点击事件也会被触发,导致下拉菜单刚打开就被window的监听器关闭。stopPropagation()阻止了这种冒泡行为。
    • dropdowns[index].classList.toggle('show'):根据当前按钮的索引index,找到对应的下拉内容,并切换其show类,从而控制显示/隐藏。
    • 关闭其他下拉菜单的逻辑:通过遍历所有下拉菜单,如果发现有其他打开的下拉菜单(即i !== currentDropdownIndex),则将其关闭。这确保了每次只有一个下拉菜单是打开的。
  3. 全局点击事件处理

    • window.addEventListener('click', ...):当用户点击页面上的任何非下拉菜单区域时,此事件将被触发。
    • 遍历所有下拉菜单,如果它们是打开的,就将它们关闭。这实现了点击页面空白处关闭所有下拉菜单的功能。

完整代码示例

将上述HTML、CSS和JavaScript代码分别保存到index.html、style.css和script.js文件中,并在HTML中正确引用它们,即可看到效果。

index.html:




    
    
    动态多下拉菜单教程
    


    

    

点击任意下拉菜单按钮,观察其独立显示和隐藏。点击页面空白处,所有下拉菜单将关闭。

style.css:

/* 引入上述CSS代码 */
nav {
  display: flex;
}

.dropbtn {
  font-size: 12px;    
  border: none;
  outline: none;
  text-align: center;
  color: #f2f2f2;
  width: 155px;
  padding: 14px 16px;
  background-color: inherit;
  font-family: serif;
  text-transform: capitalize;
  border-right: 1px solid gray;
  border-bottom: 1px solid gray;
  cursor: pointer;
}

.dropbtn:hover, .dropbtn:focus {
  background-color: #2980B9;
}

.dropdown {
  position: relative;
}

.dropdown-content {
  display: none;
  position: absolute;
  background-color: #f1f1f1;
  min-width: 200px;
  height: 200px;
  overflow: hidden;
  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
  z-index: 1;
  border-radius: 10px;
  padding-bottom: 20px;
  padding-top: 40px;
  opacity: .9;
}

.dropdown-content a {
  color: blue;
  padding: 12px 20px;
  text-decoration: none;
  display: block;
  width: 100%;
  font-size: 12px;
  background-color: transparent;
  border-bottom: 1px solid gray;
}

.dropdown a:hover {
  background-color: gray;
}

.show {
  display: block;
}

script.js:

/* 引入上述JavaScript代码 */
const dropdowns = Array.from(document.getElementsByClassName("dropdown-content"));
const dropdownButtons = Array.from(document.getElementsByClassName('dropbtn'));

let currentDropdownIndex = -1;
let openDropdownCount = 0;

dropdownButtons.forEach(function(dropdownBtn, index) {
  dropdownBtn.addEventListener('click', function(e) {
    e.stopPropagation();

    dropdowns[index].classList.toggle('show');

    currentDropdownIndex = index;

    for (let i = 0; i < dropdowns.length; i++) {
      const openDropdown = dropdowns[i];
      if (openDropdown.classList.contains('show') && i !== currentDropdownIndex) {
        openDropdown.classList.remove('show');
      }
    }

    openDropdownCount = dropdowns[index].classList.contains('show') ? 1 : 0;
  });
});

window.addEventListener('click', function(event) {
  for (let i = 0; i < dropdowns.length; i++) {
    const openDropdown = dropdowns[i];
    if (openDropdown.classList.contains('show')) {
      openDropdown.classList.remove('show');
    }
  }
  openDropdownCount = 0;
  currentDropdownIndex = -1;
});

注意事项与总结

  • 唯一ID的重要性:再次强调,id属性在整个HTML文档中必须是唯一的。如果需要引用多个相似元素,请使用类名(class)或data-*属性。
  • 事件委托:对于大量类似的交互元素,可以考虑使用事件委托(将事件监听器添加到共同的父元素上),这可以减少内存开销和DOM操作。
  • 可访问性(Accessibility):为了提高下拉菜单的可访问性,应考虑添加WAI-ARIA属性(如aria-haspopup, aria-expanded)以及键盘导航支持。
  • 性能:对于非常多的下拉菜单,频繁的DOM操作可能会影响性能。在实际项目中,可以根据需求进行优化,例如懒加载下拉内容。

通过本教程,我们学习了如何构建功能强大且行为独立的多个下拉菜单。关键在于正确地处理HTML结构、CSS定位以及JavaScript事件逻辑,特别是避免id重复、使用addEventListener和e.stopPropagation()来精确控制交互行为。掌握这些技术,可以帮助您在网页中创建更灵活、用户体验更好的交互组件。

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

540

2023.06.20

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

372

2023.07.04

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

727

2023.07.04

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

470

2023.09.01

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

391

2023.09.04

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

990

2023.09.04

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

653

2023.09.12

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

543

2023.09.20

桌面文件位置介绍
桌面文件位置介绍

本专题整合了桌面文件相关教程,阅读专题下面的文章了解更多内容。

0

2025.12.30

热门下载

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

精品课程

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

共14课时 | 0.7万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 2.7万人学习

CSS教程
CSS教程

共754课时 | 17.1万人学习

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

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号