登录
首页 >  文章 >  前端

原生JS实现多级下拉菜单:悬停与点击切换教程

时间:2025-09-17 15:36:36 229浏览 收藏

本文详细介绍了如何使用原生 JavaScript、HTML 和 CSS 创建一个响应式多级下拉菜单。该菜单能够根据设备类型提供不同的交互方式:在桌面端,菜单通过鼠标悬停展开;而在移动端,则通过点击展开。文章将深入探讨实现原理,提供清晰的代码示例,并着重强调了在不同设备上实现最佳用户体验的关键注意事项。无论你是前端新手还是经验丰富的开发者,都能从中学习到实用的技巧,掌握构建高性能、易于维护的多级下拉菜单的方法。通过本文,你将能够轻松应对各种复杂的菜单需求,提升网站的用户体验。

使用原生 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 标签,可以考虑使用 标签包裹文本,方便样式控制和语义化。例如:

<li>
    <span class="menu--item">Link</span>
    <ul class="children">
        <li>Child Link</li>
        <li>Child Link</li>
    </ul>
</li>

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

关键点:

JavaScript 交互

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

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

这段代码遍历顶级菜单项,并为每个菜单项添加点击事件监听器。当点击菜单项时,它会切换其第一个子元素(通常是子菜单 ul)的 show 类。

改进建议:

  1. 更精确的选择器: 使用更精确的选择器来避免潜在的问题。例如,可以使用 document.querySelectorAll(".menu > ul > li") 来选择顶级菜单项。
  2. 事件委托: 使用事件委托可以提高性能,尤其是在菜单项数量较多时。将事件监听器添加到父元素(例如 .menu ul),然后根据点击的目标元素来判断是否需要展开/收起子菜单。
  3. 判断屏幕尺寸: 在 JavaScript 中判断当前屏幕尺寸,只在移动端应用点击事件处理程序,避免与桌面端的悬停效果冲突。可以使用 window.innerWidth 或 window.matchMedia 来判断屏幕尺寸。

示例代码(包含改进):

document.addEventListener('DOMContentLoaded', function() {
    const menu = document.querySelector('.menu ul');

    // 仅在移动设备上启用点击事件
    if (window.innerWidth <= 1200) { // 假设1200px是移动端和桌面端的分割点
        menu.addEventListener('click', function(event) {
            const target = event.target;

            // 检查点击的是否是顶级菜单项
            if (target.tagName === 'LI' && target.parentElement === menu) {
                const subMenu = target.querySelector('ul'); // 查找子菜单

                if (subMenu) {
                    event.preventDefault(); // 阻止默认行为(例如,如果菜单项是链接)
                    subMenu.classList.toggle('show'); // 切换 show 类
                }
            }
        });
    }
});

代码解释:

总结

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

终于介绍完啦!小伙伴们,这篇关于《原生JS实现多级下拉菜单:悬停与点击切换教程》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布文章相关知识,快来关注吧!

最新阅读
更多>
课程推荐
更多>