如何使用 JavaScript 创建有下拉菜单的响应式导航栏 | 您所在的位置:网站首页 › 导航控制台如何使用 › 如何使用 JavaScript 创建有下拉菜单的响应式导航栏 |
原文: How to Build a Responsive Navigation Bar with a Dropdown Menu using JavaScript 导航栏是网站和 web 应用中经常使用的基本组件。作为一名 web 开发人员,你需要能够为客户的项目或者基本的作品集网站定制它。 在本指南中,你将学习如何只用 HTML、CSS 和 JavaScript 从头开始创建一个导航栏。你还将学习如何从无障碍方面优化它。 下面是这个导航栏的截图: ![]() 这个设计的灵感来自 Dribbble 上 Tran Mau Tri Tam 的极简导航栏。 第 1 步 - 添加 HTML 标记为了简洁起见,我们将使用一个叫做 boxicons 的图标库来为这个导航栏导入某些图标。我强烈建议使用内联 SVG。 要使用这个库,请在你的 HTML 文件的 head 插入下面的片段: 该标记被分为三个主要部分: 一个 class 为 nav-start 的 div 元素另一个 div 元素,class 为 nav-end一个 id 为 hamburger 的 button 元素所有这些元素都将被包裹在一个 header 标签中。为了更好地解释这一点,请复制下面的标记。 ![]() ![]() 对于 nav-start,我们有以下元素: 一个nav-end 有以下元素: 一个 元素,它的 role 是 search,包含一个搜索输入和搜索图标一个 class 为 btn 的按钮元素,我们将使用这个 class 来设计按钮对于汉堡包按钮: 一个按钮,其 id 和 aria-label 为 hamburger,aria-haspopup 设置为 “true”,aria-expanded 设置为 “false”。这些标签将使我们能够使这个按钮更容易被屏幕阅读器访问。下面是输出: ![]() 导航菜单 是导航链接所在的位置。用下面这个标记替换你先前添加的 nav 元素: Browse Discover Jobs Livestream About这里你有一个 nav 标签,它包含一个无序的列表,其中有五个 li 元素,代表每个导航菜单项目:browse、discover、 jobs、livestream 和 about。 前两个元素,browse 和 discover,是 button 元素,将被用来切换它们各自的下拉菜单。而元素 Jobs、livestream 和 about 只是普通的链接。 使用到目前为止的代码,你的结果应该是这样的: ![]() 接下来,让我们为每个导航按钮定义下拉元素。下面是第一个下拉菜单的标记。把你的标记中的第一个 li 元素替换成这样: BrowseShorts featured today by curators Leading creatives livestreams Gain exclusive access See trending creations Browse by apps你可以在这里获得 SVG 图标。 看看这个标记,我们添加了以下内容: 一个 id 为 dropdown1、class 为 dropdown 的 div 元素。两个 ul 元素,每个元素的 role 是 "menu"。一个 span 元素,其 class 为 dropdown-link-title,作为每个 menu 集合的 header。使用 li 和 a 标签定义的链接集合,li 标签有一个 role 为 "menuitem",每个链接有一个 class 为 dropdown-link。在每个锚标签内,通过 img 标签添加一个图标。注意:由于通过img标签添加的图标是严格意义上的声明性的,我强烈建议你直接将它们作为SVG元素添加。我这样做只是为了使代码更容易阅读 下面是第二个下拉元素dropdown2的标记: Discover Browse Categories Branding Illustration Download App MacOS & Windows Linux最终结果应该是这样: ![]() 本教程最后将提供完整的标记。 第 2 步 - 为导航栏设计样式像往常一样,我们将从重置页面上每个元素的默认 margin 和 padding 开始,添加全局变量,并对一些元素进行一些基本的样式设计。 /* style.css */ @import url("https://fonts.googleapis.com/css2?family=Inter:wght@200;300;400;500;600;700&display=swap"); * { margin: 0; padding: 0; box-sizing: border-box; font-family: "Inter", sans-serif; } :root { --dark-grey: #333333; --medium-grey: #636363; --light-grey: #eeeeee; --ash: #f4f4f4; --primary-color: #2b72fb; --white: white; --border: 1px solid var(--light-grey); --shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px, rgba(0, 0, 0, 0.08) 0px 0px 0px 1px; } body { font-family: inherit; background-color: var(--white); color: var(--dark-grey); letter-spacing: -0.4px; } ul { list-style: none; } a { text-decoration: none; color: inherit; } button { border: none; background-color: transparent; cursor: pointer; color: inherit; }接下来,添加一些可重复使用的样式。 .btn { display: block; background-color: var(--primary-color); color: var(--white); text-align: center; padding: 0.6rem 1.4rem; font-size: 1rem; font-weight: 500; border-radius: 5px; } .icon { padding: 0.5rem; background-color: var(--light-grey); border-radius: 10px; } .logo { margin-right: 1.5rem; } #nav-menu { border-bottom: var(--border); } .container { display: flex; align-items: center; justify-content: space-between; max-width: 1600px; margin: 0 auto; column-gap: 2rem; height: 90px; padding: 1.2rem 3rem; }现在你已经得到了这些基本的样式,你可以专注于核心导航栏本身的样式。 导航菜单的样式下面是为导航栏容器设计的标记: .menu { position: relative; background: var(--white); } .menu-bar li:first-child .dropdown { flex-direction: initial; min-width: 480px; } .menu-bar li:first-child ul:nth-child(1) { border-right: var(--border); } .menu-bar li:nth-child(n + 2) ul:nth-child(1) { border-bottom: var(--border); } .menu-bar .dropdown-link-title { font-weight: 600; } .menu-bar .nav-link { font-size: 1rem; font-weight: 500; letter-spacing: -0.6px; padding: 0.3rem; min-width: 60px; margin: 0 0.6rem; } .menu-bar .nav-link:hover, .dropdown-link:hover { color: var(--primary-color); } .nav-start, .nav-end, .menu-bar, .right-container, .right-container .search { display: flex; align-items: center; }下拉菜单的样式除了对下拉菜单进行样式设计外,还将使用 visibility 和 opacity 属性的组合来隐藏它。我们的想法是,只有在一个按钮被点击时才显示菜单。 .dropdown { display: flex; flex-direction: column; min-width: 230px; background-color: var(--white); border-radius: 10px; position: absolute; top: 36px; z-index: 1; visibility: hidden; opacity: 0; transform: scale(0.97) translateX(-5px); transition: 0.1s ease-in-out; box-shadow: var(--shadow); } .dropdown.active { visibility: visible; opacity: 1; transform: scale(1) translateX(5px); } .dropdown ul { display: flex; flex-direction: column; gap: 0.5rem; padding: 1.2rem; font-size: 0.95rem; } .dropdown-btn { display: flex; align-items: center; justify-content: space-between; gap: 0.15rem; } .dropdown-link { display: flex; gap: 0.5rem; padding: 0.5rem 0; border-radius: 7px; transition: 0.1s ease-in-out; } .dropdown-link p { font-size: 0.8rem; color: var(--medium-grey); }接着,可以通过使用 active class 将 visibility 和 opacity 属性恢复到默认状态来切换该菜单。但我们将通过 JavaScript 来做这件事。 如果你喜欢完全隐藏菜单,可以用 display: none; 代替 opacity 和 visibility 属性。虽然这个属性在 CSS 中不能用过渡来做动画。 右边的菜单样式接下来,为搜索输入、按钮和个人资料图片添加样式,然后在桌面屏幕上隐藏汉堡包按钮。 .right-container { display: flex; align-items: center; column-gap: 1rem; } .right-container .search { position: relative; } .right-container img { border-radius: 50%; } .search input { background-color: var(--ash); border: none; border-radius: 6px; padding: 0.7rem; padding-left: 2.4rem; font-size: 16px; width: 100%; border: var(--border); } .search .search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); opacity: 0.6; } #hamburger { display: none; padding: 0.1rem; margin-left: 1rem; font-size: 1.9rem; }现在它是这样的: ![]() 为了完成样式设计,添加媒体查询样式: @media (max-width: 1100px) { #hamburger { display: block; } .container { padding: 1.2rem; } .menu { display: none; position: absolute; top: 87px; left: 0; min-height: 100vh; width: 100vw; } .menu-bar li:first-child ul:nth-child(1) { border-right: none; border-bottom: var(--border); } .dropdown { display: none; min-width: 100%; border: none !important; border-radius: 5px; position: static; top: 0; left: 0; visibility: visible; opacity: 1; transform: none; box-shadow: none; } .menu.show, .dropdown.active { display: block; } .dropdown ul { padding-left: 0.3rem; } .menu-bar { display: flex; flex-direction: column; align-items: stretch; row-gap: 1rem; padding: 1rem; } .menu-bar .nav-link { display: flex; justify-content: space-between; width: 100%; font-weight: 600; font-size: 1.2rem; margin: 0; } .menu-bar > li:not(:last-child) { padding-bottom: 0.5rem; border-bottom: var(--border); } } @media (max-width: 600px) { .right-container { display: none; } }首先,这排列了元素,最重要的是,它定位 hamburger class 并将其隐藏。现在在平板电脑和手机屏幕上,导航栏是响应式的,汉堡包按钮是可见的。 ![]() 这就完成了导航栏的样式设计。让我们在下一节中进行功能设计。 第 3 步 - 添加 JavaScript 功能对于 JavaScript 功能,我们将专注于以下几个类别: 切换下拉菜单的可见性关闭下拉菜单切换汉堡包菜单的可见性切换 aria-expanded 属性首先,使用 DOM 的 querySelector 方法选择你的类,并将它们存储在变量中,以便它们可以重复使用。 // script.js const dropdownBtn = document.querySelectorAll(".dropdown-btn"); const dropdown = document.querySelectorAll(".dropdown"); const hamburgerBtn = document.getElementById("hamburger"); const navMenu = document.querySelector(".menu"); const links = document.querySelectorAll(".dropdown a");接下来在你的代码中添加下面的函数。我稍后将解释它们的用途。 function setAriaExpandedFalse() { dropdownBtn.forEach((btn) => btn.setAttribute("aria-expanded", "false")); } function closeDropdownMenu() { dropdown.forEach((drop) => { drop.classList.remove("active"); drop.addEventListener("click", (e) => e.stopPropagation()); }); } function toggleHamburger() { navMenu.classList.toggle("show"); }获取下拉菜单 ID下一步是获取下拉菜单的 ID。由于有两个下拉菜单,其值将基于点击的下拉按钮。 为了获得 ID,你将利用 dataset 属性,然后将该值存储到它自己的变量中。 dropdownBtn.forEach((btn) => { btn.addEventListener("click", function (e) { const dropdownIndex = e.currentTarget.dataset.dropdown; const dropdownElement = document.getElementById(dropdownIndex); console.log(dropdownElement); }); });理解这个片段: forEach 方法遍历按钮的集合addEventListener() 方法为每个按钮附加了一个点击事件currentTarget.dataset 属性获取被点击按钮的当前下拉菜单每一个 id 都被用来定位相应的下拉元素这意味着,当 dataset 为 dropdown1 的按钮被点击时,id 为 dropdown1 的 div 元素被记录到控制台,反之则为 dropdown2 按钮。 ![]() 切换菜单是相当容易的,因为你已经把下拉元素的 ID 存储到一个叫作 dropdownElement 的变量中。通过定位这个变量,你可以切换每个下拉元素的 active class。 dropdownBtn.forEach((btn) => { btn.addEventListener("click", function (e) { const dropdownIndex = e.currentTarget.dataset.dropdown; const dropdownElement = document.getElementById(dropdownIndex); dropdownElement.classList.toggle("active"); dropdown.forEach((drop) => { if (drop.id !== btn.dataset["dropdown"]) { drop.classList.remove("active"); } }); e.stopPropagation(); }); });除了切换下拉菜单外,我们还添加了一个条件,检查当前下拉元素的 id 是否与活动按钮相匹配。这可以确保每次只有一个下拉元素被展开。 ![]() aria-expanded 属性允许辅助技术告知一个交互式菜单是展开还是折叠的。要切换这个属性,请在 btn 代码块中的 e.stopPropagation() 下插入这段代码: btn.setAttribute( "aria-expanded", btn.getAttribute("aria-expanded") === "false" ? "true" : "false" );现在,只要下拉菜单是可见的,aria-expanded 属性就被设置为 true;而当菜单折叠时,它被设置为 false。 ![]() 到目前为止,下拉菜单只有在点击按钮的时候才会折叠。它应该被折叠的其他情况包括: 当点击下拉菜单内的链接时当你按下 ESC 键时当你点击文档主体时——在下拉容器之外通过调用前面创建的函数 closeDropdownMenu 和 setAriaExpandedFalse,可以折叠下拉菜单并将 aria-expanded 属性设置为 false。 // 当点击下拉链接时关闭下拉菜单 links.forEach((link) => link.addEventListener("click", () => { closeDropdownMenu(); setAriaExpandedFalse(); }) ); // 当点击文档主体时关闭下拉菜单 document.documentElement.addEventListener("click", () => { closeDropdownMenu(); setAriaExpandedFalse(); }); // 当按 ESC 键时关闭下拉菜单 document.addEventListener("keydown", (e) => { if (e.key === "Escape") { closeDropdownMenu(); setAriaExpandedFalse(); } });下面是输出: ![]() 要在平板电脑和手机屏幕上看到导航栏,请将 toggleHamburger 函数作为汉堡包按钮的回调,然后在 links 代码块内调用该函数。 links.forEach((link) => link.addEventListener("click", () => { closeDropdownMenu(); setAriaExpandedFalse(); toggleHamburger(); }) );hamburgerBtn.addEventListener("click", toggleHamburger);这会切换一个名为 show 的不同 class,控制显示或隐藏导航栏。 下面是最终的输出: ![]() 你可以添加更多下拉菜单,只需将任何一个列表项替换为按钮和下拉菜单的链接。为了使其发挥作用,请确保你更新以下内容: 根据你需要的菜单数量,更新下拉菜单的 ID。例如,第三个菜单的 ID 是 dropdown3按钮的 data-dropdown 值将设置为 dropdown3下面是一个将 Jobs 链接转换为下拉菜单的例子。 之前:Jobs之后: Jobs Software Frontend Backend AI/ML Mobile Development Others UI/UX Technical Writing这是最终的结果: ![]() 按照这个过程,你可以添加你想要的下拉菜单。 就这样,你用 HTML、CSS 和 JavaScript 成功地创建了一个带有下拉菜单的响应式导航栏。你还学会了如何使用包括 aria-expanded 属性在内的几个 aria 属性来使菜单可访问。 下面是测试这个导航栏运行情况的 CodePen 文件: 这是 GitHub 的代码链接。 总结我真诚地希望你觉得这篇文章有趣或有用。如果你这么想,请与你的朋友分享它或订阅我的博客,这样你就不会错过任何未来的文章。感谢阅读。 GitHub | Twitter | Blog | LinkedIn |
CopyRight 2018-2019 实验室设备网 版权所有 |