面试题
面试官:你别用JavaScript,就用css实现一个可以点击折叠的菜单。
我:啊?好家伙,把JavaScript给我ban了,这不是相当于绑了我的双手嘛。
面试官:能不能行?
我:能行,不行也得行。
效果展示:
面试官:恭喜你拿下一道简单题。
我:啊?头发都掉了几根了就解开了一道简单题。
纯css折叠菜单
好戏开场。
要点一:怎么实现触发点击事件?
要点二:怎么实现折叠效果?
不用JavaScript让我实现点击事件,一瞬间让我小脑萎缩。如果可以使用JavaScript,那我会设置一个点击事件,触发的处理函数就把选中时应有的样式添加上去实现折叠操作。
但是在不能使用JavaScript时则需要通过复选框实现点击事件的触发。当复选框被点击后它的checked
属性就会发生变化,按照复选框的这个特性,我们就可以通过高级css选择器选择复选框选中情况下的一些元素并且为其编辑一些样式,这样就实现了点击触发的折叠效果。
要点一
既然要用复选框实现就开整。但是用上复选框时觉得它太丑了,并且选中复选框的范围太小了,不太友好。那就需要进一步操作,把复选框给隐藏掉(在input
标签里添加hidden
属性),然后将label
标签的for
属性和复选框的id
属性设置为用一个属性值进行绑定,这样一顿操作下来既可以选中复选框,又可以让选中的范围增加到label
标签元素的大小。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>纯css菜单</title>
<link rel="stylesheet" href="./demo.css">
</head>
<body>
<div class="accordion">
<input type="checkbox" id="collapse1" hidden>
<input type="checkbox" id="collapse2" hidden>
<input type="checkbox" id="collapse3" hidden>
<article>
<label for="collapse1">点我一下</label>
<p>A</p>
</article>
<article>
<label for="collapse2">叫你点我一下</label>
<p>B</p>
<p>C</p>
</article>
<article>
<label for="collapse3">你点不点</label>
<p>D</p>
<p>E</p>
<p>F</p>
</article>
</div>
</body>
</html>
当点击这三个label
中的任何一个就可以展示所有的兄弟p
标签。
要点二
接下来就是重头戏了,精彩的CSS表演。
如果直接编写CSS代码,我觉得太麻烦了,并且也不能增加面试官的印象分,于是便使用Stylus(css预处理器)编写CSS代码。
Stylus 拥有简洁而富有表现力的语法,能省略花括号和分号,让代码更简洁清晰,还支持变量、混合、嵌套规则等,大幅减少重复代码。
使用Stylus编写CSS的准备工作:
-
全局安装Stylus。
lessnpm i -g stylus //全局安装stylus
-
创建一个
.styl
文件。 -
将创建的
.styl
文件编译为 CSS 文件,并将结果输出为.css
文件 。markdowneg: stylus demo.styl -o demo.css(修改后不会自动编译为css) stylus -w demo.styl -o demo.css(修改后会自动编译为css)
-
通过
<link>
元素引入CSS样式。html<link rel="stylesheet" href="./demo.css">
准备阶段结束了。
开始.styl
文件的编写。
Stylus 使用缩进来表示嵌套,因此保持一致的缩进非常重要,通常推荐使用 Tab进行缩进。
-
清除所有所有元素的内外边距。
stylus* margin 0 padding 0
-
给
.accordion
类设置宽度为 300 像素;给.accordion
类中article
元素的鼠标指针样式设置为指针;通过&
和+
的CSS选择器组合进行元素的选择。stylus.accordion width 300px article cursor pointer & + article margin-top 5px
& + article
:&
表示当前选择器的父元素,也就是.accordion article
选择器。+
是相邻兄弟元素选择器,用于选择紧接在另一个元素后的元素,并且它们具有相同的父元素。& + article
编译为CSS是.accordion article + article
选择器,用于选择紧接在.accordion
类下的article
元素之后的下一个article
元素。
最终实现有兄长的
article
,则加上5像素的上边距。 -
给
label
元素设置样式。stylus.accordion label display block height 40px padding 0 20px background-color #6495ED cursor pointer line-height 40px font-size: 16px color:#fff
-
给
p
元素设置样式。stylus.accordion p overflow: hidden padding 0 20px bottom 1px solid #f66 border-top none border-bottom-width 0 max-height 0 line-height 30px transition all 300ms
当菜单没有被点击时,
p
标签的最大高度设置为0;当被菜单被点击则将p
标签的最大高度修改为不是0,并且加上transition all 300ms
动画过渡属性。 -
给被选中的菜单所属的
p
标签设置高度。stylus.accordion input &:nth-child(1):checked ~ article:nth-of-type(1) p, &:nth-child(2):checked ~ article:nth-of-type(2) p, &:nth-child(3):checked ~ article:nth-of-type(3) p max-height 600px
&:nth-child(1):checked ~ article:nth-of-type(1) p
::
是伪类选择器。:checked
表示input被选中的状态,这是这个不用js实现仅靠css实现的项目的关键。:nth-child(x)
表示选择第n个孩子。:nth-of-type(n)
表示选择第n个同类型的元素。&:nth-child(1):checked ~ article:nth-of-type(1) p
编译为CSS是.accordion input:nth-child(1):checked ~ article:nth-of-type(1) p
选择器,用于选择第一个被选中的input
时,其后面同类型的article
的第一个中的所有p
元素。
实现了被点击的菜单可以展开子菜单。
最终编译成的CSS
css
* {
margin: 0;
padding: 0;
}
.accordion {
width: 300px;
}
.accordion article {
cursor: pointer;
}
.accordion article + article {
margin-top: 5px;
}
.accordion input:nth-child(1):checked ~ article:nth-of-type(1) p,
.accordion input:nth-child(2):checked ~ article:nth-of-type(2) p,
.accordion input:nth-child(3):checked ~ article:nth-of-type(3) p {
max-height: 600px;
}
.accordion label {
display: block;
height: 40px;
padding: 0 20px;
background-color: #6495ed;
cursor: pointer;
line-height: 40px;
font-size: 16px;
color: #fff;
}
.accordion p {
overflow: hidden;
padding: 0 20px;
bottom: 1px solid #6495ed;
border-top: none;
border-bottom-width: 0;
max-height: 0;
line-height: 30px;
transition: all 300ms;
}
总结
大体思路其实是很简单的,就是借用复选框被选中的特性然后结合CSS样式,但是这需要对CSS选择器足够熟悉。只要掌握了这种思想,并且熟悉一些常用的CSS选择器就可以快速解决类似的问题。