面试官:你可以用纯CSS实现折叠式菜单吗?

面试题

面试官:你别用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的准备工作:

  1. 全局安装Stylus。

    less 复制代码
    npm i -g stylus //全局安装stylus
  2. 创建一个.styl文件。

  3. 将创建的.styl文件编译为 CSS 文件,并将结果输出为 .css文件 。

    markdown 复制代码
    eg:
    	stylus demo.styl -o demo.css(修改后不会自动编译为css)
    	stylus -w demo.styl -o demo.css(修改后会自动编译为css)   
  4. 通过<link>元素引入CSS样式。

    html 复制代码
    <link rel="stylesheet" href="./demo.css">

准备阶段结束了。

开始.styl文件的编写。

Stylus 使用缩进来表示嵌套,因此保持一致的缩进非常重要,通常推荐使用 Tab进行缩进。

  1. 清除所有所有元素的内外边距。

    stylus 复制代码
    *
        margin 0
        padding 0
  2. .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像素的上边距。

  3. label元素设置样式。

    stylus 复制代码
    .accordion
        label
            display block
            height 40px
            padding 0 20px
            background-color #6495ED
            cursor pointer
            line-height 40px
            font-size: 16px
            color:#fff
  4. 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动画过渡属性。

  5. 给被选中的菜单所属的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选择器就可以快速解决类似的问题。

相关推荐
Nan_Shu_614几秒前
学习: Threejs (2)
前端·javascript·学习
G_G#8 分钟前
纯前端js插件实现同一浏览器控制只允许打开一个标签,处理session变更问题
前端·javascript·浏览器标签页通信·只允许一个标签页
@大迁世界24 分钟前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路33 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug36 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213838 分钟前
React面向组件编程
开发语言·前端·javascript
学历真的很重要39 分钟前
LangChain V1.0 Context Engineering(上下文工程)详细指南
人工智能·后端·学习·语言模型·面试·职场和发展·langchain
持续升级打怪中1 小时前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路1 小时前
GDAL 实现矢量合并
前端
hxjhnct1 小时前
React useContext的缺陷
前端·react.js·前端框架