一个由CSS+少量JS实现的自适应导航栏,小屏幕自动折叠,带动画效果。
一、缘起
PC端几乎每个页面都需要导航栏,其作用在于提供快速“导航”功能,Bootstrap的自适应导航栏能自适应折叠,美观实用,但是缺点是不够酷炫,如果你想在其中加入某些样式可能会与原始样式发生冲突,造成某些莫名其妙的冲突,而且你要使用Bootstrap的自适应导航栏必须要引入Bootstrap的CSS文件和JS文件。
以下的实现并没有参考Bootstrap的实现方式,代码也不够简洁,可能存在冗余,先看效果吧:
二、HTML结构
1 | <nav class="zj_nav"> |
其中:
menu
类的li
用于设置“汉堡菜单”;- 中间的三个
div
就是菜单的三条“横线”; nav_header
类用于显示导航栏的头部信息,这部分不会折叠,始终显示;cur_page
类用于标识当前页,样式会有所区别;- 剩下的
<li>
标签代表一个一个的导航项;
三、CSS结构
CSS一共有120行左右,当然因为我这里加入了一些动画效果所以显得多一点,下面说几个关键的地方:
导航栏的每一项都设置为
display:inline-block
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26.zj_nav {
width: 100%;
display: inline-block;
overflow: hidden;
font-size: 1.1em;
height: 3em; /*这里设置的高一些是为了显示底部的小三角*/
margin-bottom: 2em;
}
.zj_nav>ul {
display: inline-block;
width: 100%;
background: #ddd;
min-height: 2.5em;
margin: 0;
padding: 0; /*这里一定要设置padding来消除li的缩进*/
}
.zj_nav li {
list-style: none;
height: 2.5em;
line-height: 2.5em;
display: inline-block;
background: #ddd;
float: left;
}使用
not
为一般的导航项设置样式:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25.zj_nav li:not(.menu):not(.nav_header) { /*not排除含有menu和nav_header类名的导航项*/
padding: 0 1em;
position: relative;
transition: background 300ms linear;
}
.zj_nav li:not(.menu):not(.nav_header):after { /*用after伪元素做动画*/
content: "";
width: 0%;
height: 3px;
background: #48771B;
position: absolute;
right: 0;
bottom: 0;
transition: width 300ms linear;
}
.zj_nav li:not(.menu):not(.nav_header):hover:after {/*同时使用hover和after/before时需要把hover写在前面*/
width: 100%;
left: 0; /*这里设置left和前面的right相对应,实现底部动画的流畅过渡*/
}
.zj_nav>ul>li:not(.nav_header):hover {
background: #ccc;
}使用
before
伪元素为当前页设置小三角标识:1
2
3
4
5
6
7
8
9
10
11
12.zj_nav ul li.cur_page:before {
content: "";
position: absolute;
z-index: 100;
top: 2.5em;
margin-left: -8px;
width: 0;
height: 0;
border-left: 8px solid transparent; /*通过设置边框画出三角形*/
border-right: 8px solid transparent; /*transparent标识背景透明*/
border-top: 8px solid #ccc; /*这里的原理相当于使用左右边框占据部分底部边框的空间,使得顶部显示为三角形*/
}使用
@media
设置自适应折叠导航栏:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26.zj_nav>ul>li.collapse:not(.menu) {
display: inline-block;
}
@media screen and (max-width:768px) {
.zj_nav {
height: auto; /*原始高度是固定的,这里设置auto才能撑开内容*/
}
li.menu {
display: inline-block; /*显示汉堡菜单*/
}
li.collapse:not(.menu):not(.nav_header) {
display: none; /* 设置JS加入的collapse样式为隐藏,用于切换显示*/
}
.zj_nav li:not(.menu):not(.nav_header) {
display: none; /* 隐藏导航项*/
width: 100%;
border-left: 3px solid #748690;
}
.zj_nav li:not(.menu):not(.nav_header):hover:after {
width: 100%;
height: 2px;
}
.zj_nav ul li.cur_page:before {
display:none;
}
}这里有个关键技巧:使用CSS选择器的优先级来巧妙地实现导航项的显示与隐藏:
- 点击菜单会给导航项添加/删除CSS类:
collapse
; - 屏幕小于768px时含有
collapse
类的样式刚好相反,一个隐藏一个显示,优先级是media外面的大于media里面的; - 当第一次点击菜单时,导航项被添加collapse类,这时由于外面的样式优先级高,所以导航项可以显示出来;
- 当第二次点击菜单时,collapse类被移除,这时因为media内部关于导航项的样式和外部的优先级一样,但是出现的位置靠后,所以执行内部的样式,实现隐藏;
- 这时如果屏幕宽度变大,大于768px时,media内部样式失效,执行外部的样式,导航项重新显示。
- 点击菜单会给导航项添加/删除CSS类:
四、JS
因为要实现点击菜单切换显示导航栏,所以少量的JS是必要的,这里的实现使用了classList
的toggle
方法,没有考虑兼容性,一共只有12行代码:1
2
3
4
5
6
7
8
9
10
11
12
13document.getElementsByClassName("menu")[0].addEventListener("click", function(e) {
var eles;
//判断是不是LI是为了:点击li或者li中的div都能切换显示导航栏
if (e.target.tagName === "LI") {
eles = e.target.parentNode.children;
} else {
eles = e.target.parentNode.parentNode.children;
}
var len = eles.length;
for (var i = 2; i < eles.length; i++) {
eles[i].classList.toggle("collapse");
}
});