使用原生 JavaScript 实现响应式多级下拉菜单(悬停与点击切换)

轻婷大大_5917

轻婷大大_5917

2025-09-09

588人浏览

原创

使用原生 javascript 实现响应式多级下拉菜单(悬停与点击切换)

本文旨在指导开发者使用原生 JavaScript、HTML 和 CSS 构建一个响应式的多级下拉菜单。该菜单在桌面端通过鼠标悬停展开,在移动端则通过点击展开。文章将详细介绍实现思路、代码示例以及注意事项,帮助读者理解并掌握如何在不同设备上提供最佳的用户体验。

HTML 结构

首先,我们需要构建 HTML 结构。一个典型的多级下拉菜单包含一个 div 容器,内部包含一个无序列表 ul,列表项 li 可能包含嵌套的 ul 列表,形成多级菜单。

<div class="menu">
  <ul>
<li>
      Parent 1
      <ul>
<li class="link">
          <a href="#">Child 1</a>
        </li>
        <li class="link">
          <a href="#">Child 2</a>
        </li>
        <li>
          Nested child
          <ul>
<li class="link">
              <a href="#">Something</a>
            </li>
            <li class="link">
              <a href="#">Something</a>
            </li>
          </ul>
</li>
      </ul>
</li>
    <li>
      Parent 2
      <ul>
<li class="link">
          <a href="#">Basic link 1</a>
        </li>
        <li class="link">
          <a href="#">Basic link 2</a>
        </li>
        <li>
          Nested child 2
          <ul>
<li class="link">
              <a href="#">Basic nested link 1</a>
            </li>
            <li class="link">
              <a href="#">Basic nested link 2</a>
            </li>
          </ul>
</li>
      </ul>
</li>
    <li class="link">Simple Link</li>
    <li class="link">Another Link</li>
  </ul>
</div>

建议使用 标签包裹菜单项文本,以便添加链接。对于顶级 li 标签,可以考虑使用 标签包裹文本,方便样式控制和语义化。例如:


  • Link
    • Child Link
    • Child Link
  • CSS 样式

    CSS 负责菜单的布局和外观。关键点在于如何实现悬停效果和媒体查询,以便在不同屏幕尺寸下应用不同的样式。

    .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;
    }

    关键点:

    • 隐藏子菜单: 默认情况下,使用 visibility: hidden; 和 opacity: 0; 隐藏子菜单。
    • 悬停效果: 在桌面端媒体查询中,使用 :hover 伪类来改变子菜单的 visibility 和 opacity,实现悬停展开效果。
    • 移动端样式: 在移动端媒体查询中,可以调整菜单的布局,例如将其设置为垂直排列。
    • .menu ul.show: 这个CSS规则是解决问题的关键。它确保当 show 类被添加到 ul 元素时,该元素的 visibility 和 opacity 属性被正确设置,从而显示子菜单。

    JavaScript 交互

    JavaScript 用于处理移动端的点击事件,实现点击展开/收起子菜单的功能。

    let menu = document.querySelector(".menu ul");
    menu = menu.children;
    
    for (let i = 0; i <p>这段代码遍历顶级菜单项,并为每个菜单项添加点击事件监听器。当点击菜单项时,它会切换其第一个子元素(通常是子菜单 ul)的 show 类。</p><div class="aritcle_card flexRow artxards">
    											<div class="artcardd flexRow">
    												<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill10962" title="Java 接口排错助手"><img
    														src="https://img.php.cn/upload/skill/001/246/273/6adaf20dbae7c507.jpg" alt="Java 接口排错助手" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
    												<div class="aritcle_card_info flexColumn">
    													<a rel="nofollow" href="/xiazai/skill10962" title="Java 接口排错助手" class="overflowclass">Java 接口排错助手</a>
    													<p class="overflowclass">在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。</p>
    												</div>
    												<a rel="nofollow" href="/xiazai/skill10962" title="Java 接口排错助手" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
    												</a>
    											</div>
    										</div><p><strong>改进建议:</strong></p><ol>
    <li>
    <strong>更精确的选择器:</strong> 使用更精确的选择器来避免潜在的问题。例如,可以使用 document.querySelectorAll(".menu > ul > li") 来选择顶级菜单项。</li>
    <li>
    <strong>事件委托:</strong>  使用事件委托可以提高性能,尤其是在菜单项数量较多时。将事件监听器添加到父元素(例如 .menu ul),然后根据点击的目标元素来判断是否需要展开/收起子菜单。</li>
    <li>
    <strong>判断屏幕尺寸:</strong> 在 JavaScript 中判断当前屏幕尺寸,只在移动端应用点击事件处理程序,避免与桌面端的悬停效果冲突。可以使用 window.innerWidth 或 window.matchMedia 来判断屏幕尺寸。</li>
    </ol><p><strong>示例代码(包含改进):</strong></p><pre class="brush:php;toolbar:false;">document.addEventListener('DOMContentLoaded', function() {
        const menu = document.querySelector('.menu ul');
    
        // 仅在移动设备上启用点击事件
        if (window.innerWidth <p><strong>代码解释:</strong></p>
    • document.addEventListener('DOMContentLoaded', function() { ... });:确保在DOM加载完成后执行JavaScript代码。
    • window.innerWidth
    • target.tagName === 'LI' && target.parentElement === menu:检查点击的元素是否是顶级菜单项。
    • target.querySelector('ul'):查找当前菜单项的子菜单。
    • event.preventDefault():阻止默认行为,例如,如果菜单项是一个链接,阻止页面跳转。
    • subMenu.classList.toggle('show'):切换子菜单的 show 类,从而显示或隐藏子菜单。

    总结

    通过 HTML 构建菜单结构,CSS 控制菜单样式和响应式布局,JavaScript 处理移动端的点击交互,我们可以实现一个功能完善、用户体验良好的响应式多级下拉菜单。记住,清晰的结构、合理的样式和简洁的 JavaScript 代码是构建高质量 Web 应用的关键。根据实际需求,你可以进一步优化代码,例如添加动画效果、自定义样式等。

    Java免费学习笔记:立即使用
    解锁 Java 大师之旅:从入门到精通的终极指南

    相关文章

    PHP速学视频免费教程(入门到精通)
    PHP速学视频免费教程(入门到精通)

    PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

    下载

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

    相关专题

    更多
    function是什么
    function是什么

    function是函数的意思,是一段具有特定功能的可重复使用的代码块,是程序的基本组成单元之一,可以接受输入参数,执行特定的操作,并返回结果。本专题为大家提供function是什么的相关的文章、下载、课程内容,供大家免费下载体验。

    2023.08.04

    3400

    5

    js函数function用法
    js函数function用法

    js函数function用法有:1、声明函数;2、调用函数;3、函数参数;4、函数返回值;5、匿名函数;6、函数作为参数;7、函数作用域;8、递归函数。本专题提供js函数function用法的相关文章内容,大家可以免费阅读。

    2023.10.07

    574

    5

    DOM是什么意思
    DOM是什么意思

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

    2024.08.14

    6541

    7

    Vite 项目如何打包和部署
    Vite 项目如何打包和部署

    Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

    2026.10.28

    100

    16

    Vite 如何搭配 Vue.js
    Vite 如何搭配 Vue.js

    Vite搭配Vue.js专题汇集创建Vue 3项目、配置开发环境、路径别名、Vue插件、代理、多环境构建与调试的已审核文章,帮助读者完成从初始化到日常开发的配置。

    2026.10.28

    60

    15

    Vite 项目目录怎么看
    Vite 项目目录怎么看

    Vite项目目录专题汇集入口文件、src源码、public静态资源、路径别名、项目根目录和构建输出目录相关的已审核文章,帮助读者理解各文件夹的用途与引用方式。

    2026.10.28

    220

    10

    Gradle插件开发入门教程
    Gradle插件开发入门教程

    本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

    2026.10.22

    140

    11

    Gradle Wrapper配置教程
    Gradle Wrapper配置教程

    本专题介绍Gradle Wrapper的基本作用,涵盖文件组成、生成方法、版本配置和任务调用,帮助你理解项目如何使用指定的Gradle版本,减少不同开发环境带来的构建差异。

    2026.10.22

    120

    10

    Golangci-lint检查器使用教程
    Golangci-lint检查器使用教程

    本专题整理Golangci-lint常用Linters的用途与配置方法,覆盖错误处理、无效代码、安全、复杂度、命名和依赖检查,帮助项目合理选规则。

    2026.10.22

    80

    15

    热门下载

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

    精品课程

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

    共14课时 | 1.6万人学习

    Bootstrap 5教程
    Bootstrap 5教程

    共46课时 | 6.1万人学习

    CSS教程
    CSS教程

    共754课时 | 99.7万人学习