新闻中心

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

2025-10-22
浏览次数:
返回列表

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

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

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

理解常见问题与根源

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

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

核心实现原理与最佳实践

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

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

HTML结构

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

<n*>                   
    <div class="dropdown">
        <button class="dropbtn" id="active">new</button>
        <div class="dropdown-content">
            <a href="#">link 1</a>
            <a href="#">link 2</a>
            <a href="#">link 3</a>
        </div>
    </div> 

    <div class="dropdown">
        <button class="dropbtn">fresh</button>
        <div class="dropdown-content">
            <a href="#">link 4</a>
            <a href="#">link 5</a>
            <a href="#">link 6</a>
        </div>
    </div> 

    <div class="dropdown">
        <button class="dropbtn"><i>naija</i></button>
        <div class="dropdown-content">
            <a href="#">link 7</a>
            <a href="#">link 8</a>
            <a href="#">link 9</a>
        </div>
    </div> 
</n*>

注意:

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

CSS样式

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

n* {
  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; /* 用于显示下拉内容 */
}

J*aScript逻辑

J*aScript是实现下拉菜单交互的核心。我们将使用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; // 重置当前打开的下拉菜单索引
});

J*aScript代码详解:

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

    MarsCode MarsCode

    字节跳动旗下的免费AI编程工具

    MarsCode 339 查看详情 MarsCode
    • 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和J*aScript代码分别保存到index.html、style.css和script.js文件中,并在HTML中正确引用它们,即可看到效果。

index.html:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>动态多下拉菜单教程</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <n*>                   
        <div class="dropdown">
            <button class="dropbtn" id="active">new</button>
            <div class="dropdown-content">
                <a href="#">link 1</a>
                <a href="#">link 2</a>
                <a href="#">link 3</a>
            </div>
        </div> 

        <div class="dropdown">
            <button class="dropbtn">fresh</button>
            <div class="dropdown-content">
                <a href="#">link 4</a>
                <a href="#">link 5</a>
                <a href="#">link 6</a>
            </div>
        </div> 

        <div class="dropdown">
            <button class="dropbtn"><i>naija</i></button>
            <div class="dropdown-content">
                <a href="#">link 7</a>
                <a href="#">link 8</a>
                <a href="#">link 9</a>
            </div>
        </div> 
    </n*>

    <p style="margin-top: 100px; padding: 20px;">点击任意下拉菜单按钮,观察其独立显示和隐藏。点击页面空白处,所有下拉菜单将关闭。</p>

    <script src="script.js"></script>
</body>
</html>

style.css:

/* 引入上述CSS代码 */
n* {
  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:

/* 引入上述J*aScript代码 */
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定位以及J*aScript事件逻辑,特别是避免id重复、使用addEventListener和e.stopPropagation()来精确控制交互行为。掌握这些技术,可以帮助您在网页中创建更灵活、用户体验更好的交互组件。

以上就是构建动态且独立的多个下拉菜单:HTML、CSS与J*aScript实现教程的详细内容,更多请关注其它相关文章!


# 餐饮加盟品牌网站建设  # 第一个  # 自动关闭  # 单选框  # 并在  # 加载  # 只有一个  # 安阳推广营销网络  # 2018网站优化怎么做  # 转换为  # 暖通营销推广方案策划书  # 小语种网站建设选哪家  # 营销推广公司有哪些名字  # 宜宾seo网络推广引流  # 合肥肥东县企业营销推广  # 站内seo应该怎样做  # 服装行业seo推广方案  # css  # 表单  # 遍历  # 多个  #   # win  # ai  # ssl  # 懒加载  # 事件冒泡  # access  # 处理器  # js  # html  # java  # javascript 


相关栏目: 【 科技资讯46185 】 【 网络学院92790


相关推荐: Lar*el递归关系中排除子孙节点的策略  飞书妙记怎样用语音转文字速记_飞书妙记用语音转文字速记【速记方法】  DLsite中文平台入口 DLsite官网内容在线查看  在python-socketio事件处理器中安全访问Flask应用上下文  在J*a中如何在J*a中使用异常机制记录错误日志_异常日志实践经验  CSS Flexbox与媒体查询:实现响应式布局中元素的并排与堆叠  CKEditor 5 自定义构建在React应用中渲染失败的调试与解决  C++如何解决segmentation fault_C++段错误调试与原因分析  Golang如何使用context实现超时取消_Golang context超时取消模式实践  J*a实现学校排课程序_面向对象结构化项目示例  LINUX的I/O重定向是什么_深入理解LINUX中 >、>> 与 < 的区别  Promise错误处理:在catch后终止链式then执行的策略  学习通在线学习平台 学习通网页版直接进入课程中心  顺丰快递查询系统 官方正版查询入口  Go与Ruby之间实现AES加密互通:CFB模式下的密钥长度匹配策略  快手极速版在线观看 官方网页版登录地址  Composer的 "licenses" 命令如何帮助你遵守开源协议_检查项目依赖的许可证合规性  顺丰快递查单号物流信息 顺丰快递小程序查询入口  Pandas DataFrame:高效添加条件计算列  Win10怎么制作U盘启动盘 Win10系统安装U盘制作教程【详解】  Excel函数批量查找替换超快方法_Excel用REPLACE和FIND函数秒级替换  Win11怎么查看显卡显存 Win11显示适配器属性及专用视频内存查询  千牛数据看板网页版_千牛数据看板网页版访问方法  C++如何实现线程池_C++11手动实现一个简单的固定大小线程池  C++如何操作大型数据集_使用C++流式处理(Streaming)技术避免一次性加载大文件  Win11如何使用Windows Sandbox Win11沙盒功能开启与使用教程【详解】  深入理解Promise链:如何在catch后中断then的执行  漫蛙Manwa2官网入口地址分享 漫蛙漫画PC版永久访问通道  Go语言中JSON数据解码与字段访问指南  C++如何实现一个装饰器模式_C++设计模式之动态地给对象添加额外职责  Spyder启动失败:字体文件权限拒绝错误解决方案  在J*a中如何使用Stream.map转换元素_Stream映射操作解析  使用Pandas转换并合并DataFrame:多列映射至统一结构  win11怎么查看应用耗电情况 Win11电池设置查看应用能耗排行榜【优化】  React Router 嵌套组件中 URL 重定向问题的解决方案  C++如何进行游戏物理模拟_使用Box2D库为C++游戏添加2D物理效果  163邮箱官方主页登录 直达网易邮箱登录核心页面  MAC如何将整个网页截长图_MAC使用Safari的导出为PDF或第三方工具  Go语言JSON解析深度指南:动态访问与结构体映射实践  Django AJAX 文件上传教程:解决图片无法保存到模型的常见问题  知音漫客官网漫画下载_知音漫客网页版阅读记录  PS5 Pro有点优势但不多! 《燕云十六声》PS5平台与PC性能画面对比  J*aScript中高效清空DOM列表元素:解决for循环中断与任务管理问题  Python vgamepad库按键模拟:正确使用XUSB_BUTTON常量  搜狗浏览器如何使用密码生成器创建强密码 搜狗浏览器内置密码安全工具  QQ网页版官方账号入口 QQ网页版网页版登录指南  NVIDIA股价11月重挫12%:下月有望好转 但难回5万亿美元巅峰  C++ explicit关键字防止隐式转换_C++构造函数安全规范  韩小圈电脑版在线入口_网页版免费登录地址  怎样使用“本地安全策略”提升Windows安全性_Secpol.msc配置指南【高手】 

搜索