0

0

使用纯 JavaScript 实现响应式多级下拉菜单:悬停与点击的结合

DDD

DDD

发布时间:2025-09-09 18:54:13

|

639人浏览过

|

来源于php中文网

原创

使用纯 javascript 实现响应式多级下拉菜单:悬停与点击的结合

本文旨在指导开发者如何使用原生 JavaScript、HTML 和 CSS 构建一个响应式的多级下拉菜单。该菜单在桌面端采用悬停触发,而在移动端则切换为点击触发模式,无需依赖 jQuery 库。我们将重点解决移动端点击事件无法正确展开子菜单的问题,并提供代码示例和优化建议,帮助读者构建用户体验良好的导航菜单。

HTML 结构

首先,我们来看一下 HTML 的基本结构。一个 div 元素作为菜单容器,内部包含一个无序列表 ul,列表项 li 可以包含链接 a 和嵌套的子列表 ul。

CSS 样式

接下来,我们定义 CSS 样式。关键在于实现悬停效果和移动端的显示/隐藏逻辑。通过媒体查询 @media 来区分桌面端和移动端。

.menu {
  --menu-height: 40px;
  box-sizing: border-box;
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
}
.menu ul {
  list-style: none;
  padding: 16px;
  margin: 0;
}
.menu ul li,
.menu ul li a {
  opacity: 0.8;
  color: #ffffff;
  cursor: pointer;
  transition: 200ms;
  text-decoration: none;
  white-space: nowrap;
  font-weight: 700;
}
.menu ul li a,
.menu ul li a a {
  display: flex;
  align-items: center;
  height: 100%;
  width: 100%;
}
.menu ul li {
  padding-right: 36px;
}
.menu ul li::before {
  content: "";
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 5px solid #ffa500;
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
}
.menu ul .link::before {
  padding-right: 0;
  display: none;
}
.menu > ul {
  display: flex;
  height: var(--menu-height);
  align-items: center;
  background-color: #000000;
}
.menu > ul li {
  position: relative;
  margin: 0 8px;
}
.menu > ul li ul {
  visibility: hidden;
  opacity: 0;
  padding: 0;
  min-width: 160px;
  background-color: #333;
  position: absolute;
  top: 45px;
  left: 50%;
  transform: translateX(-50%);
  transition: 200ms;
  transition-delay: 200ms;
}
.menu > ul li ul li {
  margin: 0;
  padding: 8px 16px;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  height: 30px;
  padding-right: 40px;
}
.menu > ul li ul li::before {
  width: 0;
  height: 0;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  border-left: 5px solid #ffa500;
}
.menu > ul li ul li ul {
  top: -2%;
  left: 100%;
  transform: translate(0);
}

.show {
  display: flex;
}

.hide {
  display: none;
}

@media screen and (min-width: 1200px) {
  .menu ul li:hover,
  .menu ul li a:hover {
    opacity: 1;
  }
  .menu > ul li ul li:hover {
    background: black;
  }
  .menu > ul li:hover > ul {
    opacity: 1;
    visibility: visible;
    transition-delay: 0ms;
  }
}

@media screen and (max-width: 1200px) {
  .menu {
    height: 100vh;
    top: 0;
    left: 0;
    width: 20vw;
  }
  .menu ul {
    flex-direction: column;
    height: 100vh;
  }
  .menu ul li {
    margin: 1rem 0;
  }
  .menu ul li ul {
    visibility: hidden;
    opacity: 0;
    padding: 0;
    min-width: 160px;
    background-color: #333;
    position: absolute;
    top: 0px;
    left: 200%;
    transform: translateX(-50%);
    transition: 200ms;
    transition-delay: 200ms;
    height: fit-content;
  }

  .menu ul.show {
    visibility: visible;
    opacity: 1;
  }
}

关键点:

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

  • 桌面端: 使用 :hover 伪类控制子菜单的显示和隐藏 (opacity: 1; visibility: visible;)。
  • 移动端: 初始状态下,子菜单 ul 的 visibility 设置为 hidden 和 opacity 设置为 0。 通过添加 .show 类来改变 visibility 和 opacity。

JavaScript 交互

JavaScript 代码负责处理移动端的点击事件,切换子菜单的显示状态。

奇布塔
奇布塔

基于AI生成技术的一站式有声绘本创作平台

下载
let menu = document.querySelector(".menu ul");
menu = menu.children;

for (let i = 0; i < menu.length; i++) {
  console.log(menu[i]);
  menu[i].addEventListener("click", function() {
    menu[i].firstElementChild.classList.toggle("show");
    console.log(menu[i].firstElementChild);
  });
}

这段代码存在一个问题,它直接尝试给每个 li 元素添加点击事件,并切换其第一个子元素的 show 类。 这会导致只有第一层子菜单能够正常工作,更深层次的子菜单无法正确展开。

正确的做法是,首先判断当前是否是移动端,如果是,则添加点击事件,否则不添加。 并且,需要针对所有层级的 li 元素添加事件监听,并阻止事件冒泡,避免父元素和子元素同时触发点击事件。

以下是改进后的 JavaScript 代码:

document.addEventListener('DOMContentLoaded', function() {
  const menuItems = document.querySelectorAll('.menu ul li');
  const mediaQuery = window.matchMedia('(max-width: 1200px)');

  function handleMenuItemClick(event) {
    // 阻止事件冒泡,防止父元素同时触发
    event.stopPropagation();

    const subMenu = this.querySelector('ul');
    if (subMenu) {
      subMenu.classList.toggle('show');
    }
  }

  function attachEventListeners(matches) {
    menuItems.forEach(item => {
      item.removeEventListener('click', handleMenuItemClick); // 移除之前的监听器

      if (matches) {
        item.addEventListener('click', handleMenuItemClick);
      }
    });
  }

  // 初始化时执行一次
  attachEventListeners(mediaQuery.matches);

  // 监听媒体查询变化
  mediaQuery.addEventListener('change', (event) => {
    attachEventListeners(event.matches);
  });
});

代码解释:

  1. DOMContentLoaded 事件: 确保在 DOM 加载完成后执行 JavaScript 代码。
  2. querySelectorAll('.menu ul li'): 获取所有菜单项。
  3. window.matchMedia('(max-width: 1200px)'): 创建一个媒体查询对象,用于检测当前屏幕是否是移动端。
  4. handleMenuItemClick(event): 点击事件处理函数。
    • event.stopPropagation(): 阻止事件冒泡,防止点击子菜单时,父菜单也触发点击事件。
    • this.querySelector('ul'): 查找当前菜单项下的子菜单。
    • subMenu.classList.toggle('show'): 切换子菜单的 show 类,控制其显示和隐藏。
  5. attachEventListeners(matches): 根据媒体查询结果,添加或移除点击事件监听器。
    • item.removeEventListener('click', handleMenuItemClick):在添加新的监听器之前,先移除之前的监听器,避免重复绑定。
    • item.addEventListener('click', handleMenuItemClick):添加点击事件监听器。
  6. mediaQuery.addEventListener('change', (event) => { ... }): 监听媒体查询的变化,当屏幕尺寸改变时,重新添加或移除点击事件监听器。

优化建议

  • 语义化 HTML: 可以使用
  • CSS 动画: 可以使用 CSS 动画来增强菜单的过渡效果,提升用户体验。
  • 性能优化: 如果菜单项非常多,可以考虑使用事件委托来减少事件监听器的数量。
  • 使用 对于可点击的菜单项,使用

总结

通过以上步骤,我们实现了一个响应式的多级下拉菜单,可以在桌面端使用悬停,在移动端使用点击进行交互。 关键在于 CSS 样式的媒体查询和 JavaScript 的事件处理。 通过不断优化,可以创建一个用户体验良好的导航菜单。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
jquery插件有哪些
jquery插件有哪些

jquery插件有jQuery UI、jQuery Validate、jQuery DataTables、jQuery Slick、jQuery LazyLoad、jQuery Countdown、jQuery Lightbox、jQuery FullCalendar、jQuery Chosen和jQuery EasyUI等。本专题为大家提供jquery插件相关的文章、下载、课程内容,供大家免费下载体验。

150

2023.09.12

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

311

2023.10.13

jquery删除元素的方法
jquery删除元素的方法

jquery可以通过.remove() 方法、 .detach() 方法、.empty() 方法、.unwrap() 方法、.replaceWith() 方法、.html('') 方法和.hide() 方法来删除元素。更多关于jquery相关的问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

394

2023.11.10

jQuery hover()方法的使用
jQuery hover()方法的使用

hover()是jQuery中一个常用的方法,它用于绑定两个事件处理函数,这两个函数将在鼠标指针进入和离开匹配的元素时执行。想了解更多hover()的相关内容,可以阅读本专题下面的文章。

503

2023.12.04

jquery实现分页方法
jquery实现分页方法

在jQuery中实现分页可以使用插件或者自定义实现。想了解更多jquery分页的相关内容,可以阅读本专题下面的文章。

182

2023.12.06

jquery中隐藏元素是什么
jquery中隐藏元素是什么

jquery中隐藏元素是非常重要的一个概念,在使用jquery隐藏元素之前,需要先了解css样式中关于元素隐藏的属性,比如display、visibility、opacity等属性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

120

2024.02.23

jquery中什么是高亮显示
jquery中什么是高亮显示

jquery中高亮显示是指对页面搜索关键词时进行高亮显示,其实现办法:1、先获取要高亮显示的行,获取搜索的内容,再遍历整行内容,最后添加高亮颜色;2、使用“jquery highlight”高亮插件。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

176

2024.02.23

jQuery 正则表达式相关教程
jQuery 正则表达式相关教程

本专题整合了jQuery正则表达式相关教程大全,阅读专题下面的文章了解更多详细内容。

38

2026.01.13

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

142

2026.01.28

热门下载

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

精品课程

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

共14课时 | 0.8万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3万人学习

CSS教程
CSS教程

共754课时 | 24.6万人学习

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

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