今天,我们将讨论如何在不使用任何库的情况下,仅使用纯 javascript 来创建三级深度滑动移动菜单。默认情况下,我们一打开菜单就会出现第一个。另外两个将根据要求以幻灯片动画形式出现。
准备好迎接另一个挑战了吗?
我们的多级菜单
检查下面笔上的菜单实现:
See the Pen How to Build an Animated Multilevel Mobile Menu With JavaScript by Envato Tuts+ (@tutsplus) on CodePen.
1.定义 html 标记
我们的移动菜单的标记将包含以下元素:
一个里面 header有一个。nav
我们页面的main主要内容所在的位置。
在 里面nav,我们会放两个divs。第一个将有header-bar课程,而第二个将有menu-wrapper一个。
遗嘱包括.header-bar三个要素:
切换菜单
公司标志
该公司的推特账号
在 中.menu-wrapper,我们将div在类中放置三个 s list-wrapper。为简单起见,我们将它们称为面板。注意事项:
在第一个面板中,我们将指定包含父菜单项和子菜单项的菜单结构。为此,我们将使用带有嵌套无序列表的典型标记。
第二个和第三个面板将包含一个后退按钮和一个空div的sub-menu-wrapper. 更多关于他们的工作:
后退按钮将帮助我们上一层楼。也就是说,从三级到二级,从二级到一级。
第二.sub-menu-wrapper个面板的 将包含第二级链接。同理,.sub-menu-wrapper第三个面板的 将保存第三级链接。我们将通过 JavaScript 动态插入这些链接。
考虑到以上所有内容,出现以下标记:
<header class="page-header">
<nav>
<div class="header-bar">
<button class="toggle-menu" type="button">
MENU
</button>
<a href="" class="brand">BRAND</a>
<a href="" class="social" target="_blank" title="">
<svg xmlns="https://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z" />
</svg>
</a>
</div>
<div class="menu-wrapper">
<div class="list-wrapper">
<ul class="menu level-1">
<li>
<a href="" class="nested">Categories </a>
<ul class="sub-menu level-2">
<li>
<a href="" class="nested">Living Room </a>
<ul class="sub-menu level-3">...</ul>
</li>
<li>
<a href="">Dining Room</a>
</li>
...
</ul>
</li>
<li>
<a href="" class="nested">Featured Products</a>
<ul class="sub-menu level-2">...</ul>
</li>
...
</ul>
</div>
<div class="list-wrapper">
<button type="button" class="back-one-level">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24">
<path d="M16.67 0l2.83 2.829-9.339 9.175 9.339 9.167-2.83 2.829-12.17-11.996z" />
</svg>
<span>Back</span>
</button>
<div class="sub-menu-wrapper"></div>
</div>
<div class="list-wrapper">
<button type="button" class="back-one-level">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24">
<path d="M16.67 0l2.83 2.829-9.339 9.175 9.339 9.167-2.83 2.829-12.17-11.996z" />
</svg>
<span>Back</span>
</button>
<div class="sub-menu-wrapper"></div>
</div>
</div>
</nav>
</header>
<main class="page-main">...</main>
2.指定主要样式
现在让我们专注于移动菜单的标题样式。
为简单起见,我不会介绍所有样式,但可以通过单击演示项目的 css 选项卡随意查看它们。
需要注意的一些事项:
标题将是一个固定定位的元素,最大宽度为 600 像素。
将.header-bar具有 60px 的固定高度。
将.menu-wrapper绝对定位并位于.header-bar. 此外,它将具有等于视口高度减去.header-bar' 高度的高度。最后,它最初会被隐藏。
相关样式:
/*CUSTOM VARIABLES HERE*/
.page-header {
position: fixed;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 100%;
max-width: 600px;
margin: 0 auto;
color: var(--white);
}
.page-header .header-bar {
display: flex;
justify-content: space-between;
align-items: center;
height: 60px;
padding: 0 20px;
background: var(--header-bar-bg);
}
.page-header .menu-wrapper {
display: none;
position: absolute;
top: 60px;
left: 0;
width: 100%;
height: calc(100vh - 60px);
overflow: hidden;
}
让我们继续面板样式。
他们都将获得父母的身高并拥有overflow-y: auto. 此属性确保滚动条将在其中有很多菜单链接的情况下出现。
尤其是第二个和第三个面板默认情况下将绝对定位并超出屏幕。
相关样式:
/*CUSTOM VARIABLES HERE*/
.page-header .list-wrapper {
height: 100%;
padding: 30px 20px;
overflow-y: auto;
background: var(--menu-bg);
}
.page-header .list-wrapper:nth-child(2),
.page-header .list-wrapper:nth-child(3) {
position: absolute;
top: 0;
left: 0;
right: 0;
transform: translateX(100%);
backface-visibility: hidden;
transition: transform 0.5s;
}
接下来,我们将隐藏第一个和第三个面板中的所有嵌套菜单:
.page-header .list-wrapper:nth-child(1) > ul > li > .sub-menu,
.page-header .list-wrapper:nth-child(2) .level-3 {
display: none;
}
接下来,我们将为移动菜单链接设置一些样式,特别是:
打开嵌套菜单的链接将带有下划线。
为了指示链接处于活动状态或悬停状态,我们将为其赋予不同的颜色和字符。
相关样式:
/*CUSTOM VARIABLES HERE*/
.page-header .menu-wrapper a {
display: inline-block;
position: relative;
padding: 5px 0;
}
.page-header .menu-wrapper a.nested {
text-decoration: underline;
}
.page-header .menu-wrapper a:hover,
.page-header .menu-wrapper a.is-active {
color: var(--orange);
}
.page-header .menu-wrapper a:hover::before,
.page-header .menu-wrapper a.is-active::before {
content: "✦";
position: absolute;
top: 50%;
right: -20px;
transform: translateY(-50%);
color: var(--orange);
}
.page-header .back-one-level {
display: flex;
align-items: center;
margin-bottom: 40px;
}
最后,我们将为后退按钮指定一些简单的样式。
他们来了:
/*CUSTOM VARIABLES HERE*/
.page-header .back-one-level {
display: flex;
align-items: center;
margin-bottom: 40px;
}
.page-header .back-one-level svg {
fill: var(--white);
margin-right: 10px;
}
3.添加javaScript
设置样式后,是时候讨论使用幻灯片动画显示嵌套菜单级别所需的操作了。
切换菜单
这是一个动画 GIF,说明了菜单的切换状态:
每次单击切换按钮时,我们都会执行以下操作:
通过类切换菜单的可见性is-visible。如果它被隐藏,它将出现,反之亦然。
检查菜单是否关闭。如果满足此条件,我们将执行以下操作:
is-visible如果有的话,从第二个和第三个面板中删除该类。
is-active如果存在此类元素,请从活动菜单链接中删除该类。
这是所需的 JavaScript 代码:
const pageHeader = document.queryselector(".page-header");
const toggleMenu = pageHeader.querySelector(".toggle-menu");
const menuWrapper = pageHeader.querySelector(".menu-wrapper");
const listWrapper2 = pageHeader.querySelector(".list-wrapper:nth-child(2)");
const listWrapper3 = pageHeader.querySelector(".list-wrapper:nth-child(3)");
const isVisibleClass = "is-visible";
const isActiveClass = "is-active";
toggleMenu.addeventListener("click", function () {
// 1
menuWrapper.classList.toggle(isVisibleClass);
// 2
if (!this.classList.contains(isVisibleClass)) {
// 1
listWrapper2.classList.remove(isVisibleClass);
listWrapper3.classList.remove(isVisibleClass);
// 2
const menuLinks = menuWrapper.querySelectorAll(".is-active");
for (const menuLink of menuLinks) {
menuLink.classList.remove(isActiveClass);
}
}
});
以及相关样式:
.page-header .menu-wrapper.is-visible {
display: block;
}
.page-header .list-wrapper:nth-child(2),
.page-header .list-wrapper:nth-child(3) {
transition: transform 0.5s;
}
.page-header .list-wrapper:nth-child(2).is-visible,
.page-header .list-wrapper:nth-child(3).is-visible {
transform: none;
}
开放二级
这是一个动画 GIF,说明了二级菜单的显示方式:
每次我们单击第一级(可见的)菜单链接时,我们都会检查它是否有嵌套菜单作为兄弟。如果满足此条件,我们将执行以下操作:
阻止其默认操作。
将is-active班级分配给它。
创建其兄弟的深层副本。
将这个新创建的节点附加到.sub-menu-wrapper第二个面板中。
用幻灯片动画显示第二个面板。
这是所需的 JavaScript 代码:
...
for (const level1Link of level1Links) {
level1Link.addEventListener("click", function (e) {
const siblingList = level1Link.nextElementSibling;
if (siblingList) {
// 1
e.preventDefault();
// 2
this.classList.add(isActiveClass);
// 3
const cloneSiblingList = siblingList.clonenode(true);
// 4
subMenuWrapper2.innerHTML = "";
subMenuWrapper2.append(cloneSiblingList);
// 5
listWrapper2.classList.add(isVisibleClass);
}
});
以及动画的相关样式:
.page-header .list-wrapper:nth-child(2) {
transition: transform 0.5s;
}
.page-header .list-wrapper:nth-child(2).is-visible {
transform: none;
}
三级开放
这是一个动画 GIF,说明了第三级菜单的显示方式:
请记住,默认情况下,第二个面板中没有任何内容.sub-menu-wrapper。在上一节中,我们描述了它如何通过执行深拷贝来接收内容。
这是生成的标记的示例:
接下来,我们必须在有人单击二级菜单链接时立即执行一些操作。然而,棘手的是这些链接是动态生成的,并不是初始 dom 的一部分。也就是说,该click事件不适用于这些元素:(
令人高兴的是,解决方案很简单。感谢事件委托,我们将click事件附加到作为 DOM 一部分的父面板。然后,通过该target事件的属性,我们将检查发生事件的元素,以确保这些元素是与子菜单兄弟的链接。
假设这些是目标元素,对于它们中的每一个,我们将执行以下操作(类似于上一节):
阻止其默认操作。
将is-active班级分配给它。
创建其兄弟的(深)副本。请注意,无论我们是否进行深拷贝,都不会有任何变化,因为我们的菜单包含三个级别。
将这个新创建的节点附加到.sub-menu-wrapper第三个面板中。
用幻灯片动画显示第三个面板。
这是所需的 JavaScript 代码:
...
listWrapper2.addEventListener("click", function (e) {
const target = e.target;
if (target.tagName.toLowerCase() === "a" && target.nextElementSibling) {
const siblingList = target.nextElementSibling;
// 1
e.preventDefault();
// 2
target.classList.add(isActiveClass);
// 3
const cloneSiblingList = siblingList.cloneNode(true);
// 4
subMenuWrapper3.innerHTML = "";
subMenuWrapper3.append(cloneSiblingList);
// 5
listWrapper3.classList.add(isVisibleClass);
}
});
以及动画的相关样式:
.page-header .list-wrapper:nth-child(3) {
transition: transform 0.5s;
}
.page-header .list-wrapper:nth-child(3).is-visible {
transform: none;
}
返回一级
这是一个动画 GIF,说明了后退按钮的工作原理:
每次单击后退按钮时,我们将执行以下操作:
找到目标后退按钮的父面板并删除其is-visible类。
从父面板的前一个兄弟的活动链接中删除is-active该类。
这是所需的 JavaScript 代码:
...
for (const backOneLevelBtn of backOneLevelBtns) {
backOneLevelBtn.addEventListener("click", function () {
// 1
const parent = this.closest(".list-wrapper");
parent.classList.remove(isVisibleClass);
// 2
parent.previousElementSibling
.querySelector(".is-active")
.classList.remove(isActiveClass);
});
}
再一次,这个动作有一些伴随的 CSS 样式。
结论
在本教程中,我们利用一些常见的 CSS 样式和 JavaScript DOM 的 api 创建了一个多级移动菜单。最重要的是,我们的初始标记包含用于创建菜单的简单嵌套列表。这意味着我们可以将其转换为动态的,并利用 CMS 的功能,如 wordpress,只需进行一些修改。
- 2.指定主要样式
- 3.添加javaScript
- 切换菜单
- 开放二级
- 三级开放
- 返回一级