大家好,我是宝哥。
今天讲50个项目50天,前端实战12:手风琴效果。
本项目是一个常见的面板式问答交互组件,也被称为手风琴效果。点击问题标题可以展开或收起对应的答案内容。项目注重于简洁的设计和易用性,使用纯 CSS 实现展开/收起的动画效果。
源码下载地址
https://github.com/bradtraversy/50projects50days/tree/master/faq-collapse
在线预览(文末原文链接直达):
前端实战项目系列正在更新:12/50
- 01:可展开卡片
- 02:进度条
- 03:旋转导航动画
- 04:隐藏的搜索小组件
- 05:模糊加载效果
- 06:滚动动画
- 07:拆分页面
- 08:表单输入框波纹
- 09:音频播放按钮面板
- 10:简洁的冷笑话生成器
- 11:事件键盘码
- 12:手风琴效果
基本原理
- 项目通过 HTML 结构定义多个问答内容区块 (
div.faq
)。 - 每个问答区块包含一个问题标题 (
h3.faq-title
) 和一个答案内容 (p.faq-text
)。 - 还包含一个按钮 (
button.faq-toggle
),用来控制答案内容的展开和收起。 - 按钮的图标 (
<i>
) 使用 Font Awesome 字体图标库。 - CSS 样式定义了问答区块的样式、按钮的样式以及展开/收起的动画效果。
- JavaScript 代码通过遍历所有按钮 (
faq-toggle
),为每个按钮添加点击事件监听器。 - 点击按钮时,会切换其父元素 (
faq
) 的类名 (active
),从而控制答案内容的显示和隐藏,并根据状态切换按钮的图标。
HTML 结构
- 首先包含一个标题 (
<h1>
),用来显示 “Frequently Asked Questions” (常见问题解答)。 - 然后,包含一个容器 (
div.faq-container
),用来容纳所有的问答区块。 - 每个问答区块 (
div.faq
) 包含以下内容:- 问题标题 (h3.faq-title
)- 答案内容 (p.faq-text
)- 控制按钮 (button.faq-toggle
)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" integrity="sha512-1PKOgIY59xJ8Co8+NE6FZ+LOAZKjy+KY8iq0G4B3CyeY6wYHN3yt9PW0XpSriVlkMXe40PTKnXrLnZ9+fkDaog==" crossorigin="anonymous" />
<link rel="stylesheet" href="style.css" />
<title>FAQ</title>
</head>
<body>
<h1>Frequently Asked Questions</h1>
<div class="faq-container">
<div class="faq active">
<h3 class="faq-title">
Why shouldn't we trust atoms?
</h3>
<p class="faq-text">
They make up everything
</p>
<button class="faq-toggle">
<i class="fas fa-chevron-down"></i>
<i class="fas fa-times"></i>
</button>
</div>
<div class="faq">
<h3 class="faq-title">
What do you call someone with no body and no nose?
</h3>
<p class="faq-text">
Nobody knows.
</p>
<button class="faq-toggle">
<i class="fas fa-chevron-down"></i>
<i class="fas fa-times"></i>
</button>
</div>
<div class="faq">
<h3 class="faq-title">
What's the object-oriented way to become wealthy?
</h3>
<p class="faq-text">
Inheritance.
</p>
<button class="faq-toggle">
<i class="fas fa-chevron-down"></i>
<i class="fas fa-times"></i>
</button>
</div>
<div class="faq">
<h3 class="faq-title">
How many tickles does it take to tickle an octopus?
</h3>
<p class="faq-text">
Ten-tickles!
</p>
<button class="faq-toggle">
<i class="fas fa-chevron-down"></i>
<i class="fas fa-times"></i>
</button>
</div>
<div class="faq">
<h3 class="faq-title">
What is: 1 + 1?
</h3>
<p class="faq-text">
Depends on who are you asking.
</p>
<button class="faq-toggle">
<i class="fas fa-chevron-down"></i>
<i class="fas fa-times"></i>
</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
CSS 样式
- 定义整体的样式 (body, h1)。
- 设置问答区块的样式 (
div.faq
),包括背景色、边框、圆角、间距、填充等。 - 关键在于使用
transition
属性定义内容展开/收起过程的动画效果。 - 另外定义激活状态下的问答区块样式 (
faq.active
)。- 使用伪元素 (::before
和::after
) 在激活状态下添加额外的装饰性图标。- 设置问题标题 (h3.faq-title
) 和 答案内容 (p.faq-text
) 的样式。- 设置按钮的样式 (button.faq-toggle
),并定义激活状态下按钮图标的切换 (faq.active .faq-toggle
).
@import url('https://fonts.googleapis.com/css?family=Muli&display=swap');
* {
box-sizing: border-box;
}
body {
font-family: 'Muli', sans-serif;
background-color: #f0f0f0;
}
h1 {
margin: 50px 0 30px;
text-align: center;
}
.faq-container {
max-width: 600px;
margin: 0 auto;
}
.faq {
background-color: transparent;
border: 1px solid #9fa4a8;
border-radius: 10px;
margin: 20px 0;
padding: 30px;
position: relative;
overflow: hidden;
transition: 0.3s ease;
}
.faq.active {
background-color: #fff;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1), 0 3px 6px rgba(0, 0, 0, 0.1);
}
.faq.active::before,
.faq.active::after {
content: '\f075';
font-family: 'Font Awesome 5 Free';
color: #2ecc71;
font-size: 7rem;
position: absolute;
opacity: 0.2;
top: 20px;
left: 20px;
z-index: 0;
}
.faq.active::before {
color: #3498db;
top: -10px;
left: -30px;
transform: rotateY(180deg);
}
.faq-title {
margin: 0 35px 0 0;
}
.faq-text {
display: none;
margin: 30px 0 0;
}
.faq.active .faq-text {
display: block;
}
.faq-toggle {
background-color: transparent;
border: 0;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
padding: 0;
position: absolute;
top: 30px;
right: 30px;
height: 30px;
width: 30px;
}
.faq-toggle:focus {
outline: 0;
}
.faq-toggle .fa-times {
display: none;
}
.faq.active .faq-toggle .fa-times {
color: #fff;
display: block;
}
.faq.active .faq-toggle .fa-chevron-down {
display: none;
}
.faq.active .faq-toggle {
background-color: #9fa4a8;
}
JavaScript 代码
- 获取所有控制按钮的 DOM 元素集合 (
querySelectorAll
) - 遍历每个按钮,并为其添加点击事件监听器。
- 点击按钮时,触发事件监听器函数,该函数通过切换父元素 (
faq
) 的类名 (active
) 来控制内容的显隐,并根据状态调整按钮图标。
const toggles = document.querySelectorAll('.faq-toggle')
toggles.forEach(toggle => {
toggle.addEventListener('click', () => {
toggle.parentNode.classList.toggle('active')
})
})
总结
本项目中,巧妙地利用了伪元素在激活状态下呈现额外的装饰性图标,配合
transition
属性的设定,实现了内容展开/收起的视觉反馈效果。
在线预览(点底部原文链接可直达):https://qdkfweb.cn/50projects50days/faq-collapse/
最后
如果你觉得宝哥今天的尝试对你有帮助,就给我点个赞,关注一波。分享出去,也许你的转发能给别人带来一点启发。
推荐链接
10张脑图带你快速入门Vue3 | 附高清原图
前端程序员简历模板整理和下载
小抄学习法: 4张图掌握JS核心要点
分享和在看就是最大的支持❤️
版权归原作者 前端开发博客 所有, 如有侵权,请联系我们删除。