CSS & JS Effect – FAQ Accordion & Slide Down
效果
参考:
Youtube – Responsive FAQ accordion dropdown | HTML and CSS Tutorial
几个难点
1. 如何 align left for +- icon.
2. 如何实现点击
3. 如何实现 slide down
一步一步做
Semantic HTML
<div class="container">
<details>
<summary>question 1</summary>
<p>answer</p>
</details> <details>
<summary>question 2</summary>
<p>answer</p>
</details>
</div>
Semantic 处理这种信息一般上是用 details > sumary + p
效果
默认就自带效果了, 但是这不是预期要的.
左边的小箭头来自于 summary display: list-item, 它是一个 marker 来的.
HTML
<div class="container">
<details class="faq" open>
<summary class="question">
question 1<i class="plus-icon fa-solid fa-plus"></i
><i class="minus-icon fa-solid fa-minus"></i>
</summary>
<div class="answer">
<p>answer</p>
</div>
</details>
</div>
加上了 plus / minus icon, 还有各个 class selector
details 的 open attribtue 是为了要平仓掉默认的功能.
完整版 HTML


<div class="container">
<details class="faq" open>
<summary class="question">
Q. Can CoolMan attend to my aircon issue at the time I want?<i
class="plus-icon fa-solid fa-plus"
></i
><i class="minus-icon fa-solid fa-minus"></i>
</summary>
<div class="answer">
<p>
Yes, we can perform the repairing works at your desired timing, but
note that our repair schedule varies depending on the job complexity
and volumes. To avoid disappointment, we strongly recommend that you
book an appointment with us through ONLINE BOOKING.
</p>
</div>
</details>
<details class="faq" open>
<summary class="question">
Q. What’s wrong with my aircon? It doesn’t turn on at all.<i
class="plus-icon fa-solid fa-plus"
></i
><i class="minus-icon fa-solid fa-minus"></i>
</summary>
<div class="answer">
<p>
Check whether AC power gets to your aircon. If there’s no power,
check the fuses or MCB circuit breakers. If there’s still no power,
do contact our qualified technician to rectify the issue for you.
</p>
</div>
</details>
</div>
CSS Style
.container {
display: flex;
flex-direction: column;
gap: 2rem;
width: 576px;
margin-inline: auto; .faq {
--answer-scroll-height: 0;
cursor: pointer;
.question {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem; .minus-icon {
display: none;
}
} .answer {
height: 0;
overflow: hidden;
transition: height 0.4s;
} &.opened {
.question {
.minus-icon {
display: block;
}
.plus-icon {
display: none;
}
}
.answer {
height: var(--answer-scroll-height);
}
}
}
}
上面提到了 3 个难点
1. 如何 align left for +- icon
使用 Flex.
还有一种做法是使用 :after 做出 icon, 然后绝对定位 rigth: 0, 在加上 padding-right 做空间. 但我觉得这种方法非常不直观. 还是用 Flex 好一些.
2. 如何实现点击
用 JS, 点击的时候添加一个 class opened, CSS 依赖这个 class 做效果
还有一种做法是用 anchor + href # hash + :target 来实现 (上面参考的视频). 但是我个人觉得用 JS 更直观一些.
3. 如何实现 slide down
用 max-height, 但是会遇到 CSS 冷知识 – Transition 对 height: auto / fit-content 无效 的问题.
解决方法是用 JS 获取 scroll height 然后通过 CSS variables 传进来, (上面参考视频的方式是给一个大约的值, 这样做的好处是不需要 JS, 坏处就是不太准确)
注: 如果用 JS 传 CSS variables 那就不需要用 max-height 了, 可以直接用 height.
Javascript
// 清除 HTML 默认 details 行为
document.querySelectorAll("details > summary").forEach((el) => {
el.addEventListener("click", (e) => e.preventDefault());
}); const faqs = Array.from(document.querySelectorAll(".faq"));
for (const faq of faqs) {
const answer = faq.querySelector(".answer");
faq.style.setProperty("--answer-scroll-height", answer.scrollHeight + "px"); // 注入每一个的 scroll height faq.addEventListener("click", () => {
faqs.forEach((f) => f.classList.remove("opened")); // 关闭所有
faq.classList.add("opened"); // 开启当前这一个
});
}
补上一个场景难题: 动态内容
上面我们用的方式是在 slide down 的时候给 element height. 这样会衍生一个问题. 那就是 element 丢失了 height: auto 原有的特性 hug content.
所以当内容增加的时候, 整个 container 的 height 依旧是打开时候的 height. 解决思路很简单就是把 height set 成 auto. 只是要控制好时机.
在 transitionend 的时候把 height set 成 auto. 这样就 hug content 了.
cardWrapper.addEventListener('transitionend', () => {
if (cardWrapper.style.height !== '') { // 只有开的时候 set auto 哦, 关闭的时候不要 set 哦
cardWrapper.style.height = 'auto';
}
});
但马上会发现关闭的时候出问题了.
因为 height: auto -> 0 是触发不了 transition 的, 所以在 close 的时候我们需要先把它 set 回去 height: scrollHeight 等渲染后再 set 成 0
closeBtn.addEventListener('click', () => {
if (cardWrapper.style.height === 'auto') { // auto 代表已经 opened 了
cardWrapper.style.height = `${cardWrapper.scrollHeight}px`;
requestAnimationFrame(() => {
cardWrapper.style.removeProperty('height');
});
} else { // 没有 auto 代表还没有 slide 完 user 就已经点击 close 了. 这时只要 remove height 就可以了
cardWrapper.style.removeProperty('height');
}
});
至此 slide down 就支持动态内容了
测试代码
HTML


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="style.css" />
<script src="https://kit.fontawesome.com/046b7760d4.js" crossorigin="anonymous" defer></script>
<script src="./bundle.js" defer></script>
</head>
<body>
<div class="container">
<div class="action">
<button class="open-btn">open</button>
<button class="close-btn">close</button>
</div>
<div class="card-wrapper">
<div class="card">
<h1 class="title">Hello World</h1>
<p class="description">Lorem ipsum dolor sit amet consectetur adipisicing elit. Minima, voluptatibus.</p>
<button class="add-more-btn">add more</button>
</div>
</div>
</div>
</body>
</html>
CSS


* {
padding: 0;
margin: 0;
box-sizing: border-box;
} body {
.container {
margin-top: 30vh;
max-width: 1280px;
margin-inline: auto; button {
border-width: 0;
background-color: transparent;
border: 1px solid red;
padding: 0.5rem 1rem;
cursor: pointer;
} display: flex;
gap: 2rem; .action {
display: flex;
flex-direction: column;
gap: 1rem;
}
.card-wrapper {
height: 0;
overflow-y: hidden;
transition: height 1s; .card {
background-color: pink;
padding: 1rem; .title {
font-size: 1.5rem;
margin-bottom: 1rem;
}
.description {
line-height: 1.5;
white-space: pre-line;
}
.add-more-btn {
margin-top: 1rem;
}
}
}
}
}
JavaScript


const openBtn = document.querySelector('.open-btn')!;
const cardWrapper = document.querySelector<HTMLElement>('.card-wrapper')!;
openBtn.addEventListener('click', () => {
cardWrapper.style.height = `${cardWrapper.scrollHeight}px`;
}); const closeBtn = document.querySelector('.close-btn')!;
closeBtn.addEventListener('click', () => {
if (cardWrapper.style.height === 'auto') {
cardWrapper.style.height = `${cardWrapper.scrollHeight}px`;
requestAnimationFrame(() => {
cardWrapper.style.removeProperty('height');
});
} else {
cardWrapper.style.removeProperty('height');
}
}); cardWrapper.addEventListener('transitionend', () => {
if (cardWrapper.style.height !== '') {
cardWrapper.style.height = 'auto';
}
}); const addMoreBtn = document.querySelector('.add-more-btn')!;
const description = document.querySelector('.description')!;
addMoreBtn.addEventListener('click', () => {
description.textContent = `${description.textContent}\n${description.textContent}`;
});
补上一个场景难题: Flex item slide down
中间这个 message 一开始是 hidden 的, 在 submit succeeded 后才 slide down.
它一开始必须是 display:none 不然会导致 double gap
因为即便 height: 0, visibility: hidden. element 依然是存在的, 那么就会有 gap
所以必须用 display: none, 而 display: none 会导致无法获取到 scrollHeight.
所以我们不可以像上面例子中那样, 提前去拿 scrollHeight, 而是要等到 display: block 了以后才获取 scrollHeight set CSS variable.
CSS variable 的改变是会触发 transition 的. 不必担心. 类似这样:
冷知识 – <details> display: flex 无效
如果想在 question 和 answer 中制造间距, 就只能使用 margin-top 或 margin-bottom 了. 用不到 gap.
不管是给 question margin-bottom 还是 answer margin-top. 关闭时看上去都很奇怪.
中间大片空白就是 answer 的间距. 正确的做法应该是只有在开启的时候才给 margin-top.
Accordion 搭配 location hash
accordion 开关是 JavaScript 控制的, 所以当页面刷新后, 它会丢失掉之前的记录 (游览器不知道刷新前打开着哪一个)
这个体验通常是没有问题的, 但如果想解决它也不难. 可以使用 History API
步骤
1. 为每一个 item 配置一个 id 和 data-location-hash="someUniqueId"
2. 打开时添加 hash 到 URL 上
const itemLocationHash = item.dataset.locationHash!;
history.replaceState(undefined, '', `#${itemLocationHash}`);
3. 关闭时 remove hash from URL
history.replaceState(undefined, '', `${location.pathname}${location.search}`);
4. 当 page load 的时候判断是否开启 base on URL hash
if (location.hash !== '' && location.hash.substring(1) === itemLocationHash) {
open();
}
开关效果
refresh 效果
CSS & JS Effect – FAQ Accordion & Slide Down的更多相关文章
- HTML---引入css,js | 常用标签示例
一.前端基础包括哪些?如何理解 二.css,js引入_及head中其他标签 三.特殊符号 四.常见的标签 4.1,form表单 4.2,input系列(单选框.复选框.input传文件.重置) 4.3 ...
- CSS & JS 制作滚动幻灯片
==================纯CSS方式==================== <!DOCTYPE html> <html> <head> <met ...
- 【转】Maven Jetty 插件的问题(css/js等目录死锁)的解决
Maven Jetty 插件的问题(css/js等目录死锁,不能自动刷新)的解决: 1. 打开下面的目录:C:\Users\用户名\.m2\repository\org\eclipse\jetty ...
- Css Js Loader For Zencart
Css Js Loader 描述:这个插件很早就出来了,可能知道人非常少 这个插件的功能是整合所有的网站的CSS和JS内容到一个文件里边. 因为CSS和JS文件到了一个文件,加快了程序的运行 在配合其 ...
- 购物车数字加减按钮HTML+CSS+JS(有需要嫌麻烦的小伙伴拿走不谢)
之前在写详情页的时候,如下图 因为自己嫌麻烦,就去看其他网站是怎么写的,想直接拿来用,后来看来看去觉得写得很麻烦,于是最后还是决定自己写,附上HTML+CSS+JS代码,一条龙一站式贴心服务2333 ...
- vs合并压缩css,js插件——Bundler & Minifier
之前做了一个大转盘的抽奖活动,因为比较火,部分用户反馈看不到页面的情况,我怀疑js加载请求过慢导致,所以今天针对之前的一个页面进行调试优化. 首先想到的是对页面的js和css进行压缩优化,百度了下vs ...
- nginx资源定向 css js路径问题
今天玩玩项目,学学nginx发现还不错,速度还可以,但是CSS JS确无法使用,原来Iginx配置时需要对不同类型的文件配置规则,真是很郁闷,不过想想也还是很有道理.闲暇之际,把配置贴上来.#user ...
- IIS7的集成模式下如何让自定义的HttpModule不处理静态文件(.html .css .js .jpeg等)请求
今天将开发好的ASP.NET站点部署到客户的服务器上后,发现了一个非常头疼的问题,那么就是IIS7的应用程序池是集成模式的话,ASP.NET项目中自定义的HttpModule会处理静态文件(.html ...
- 网站加载css/js/img等静态文件失败
网站加载css/js/img等静态文件失败,报网站http服务器内部500错误.而服务器中静态文件存在且权限正常. 从浏览器中直接访问文件,出来乱码.这种问题原因在于iis中该网站mime配置报错,不 ...
- 【前端】Sublime text3 插件HTML/CSS/JS prettify 格式化代码
1.首先安装插件 菜单的preference->packages control,然后输入install .. 回车,再输入HTML/CSS/JS prettify 再回车,重启后就可以了. 2 ...
随机推荐
- 解码 xsync 的 map 实现
解码 xsync 的 map 实现 最近在寻找 Go 的并发 map 库的时候,翻到一个 github 宝藏库,xsync (https://github.com/puzpuzpuz/xsync) . ...
- Django集成的密码找回功能
要实现忘记密码功能,您需要进行以下修改: 添加忘记密码链接到登录页面. 创建密码丢失修改页面. 创建密码修改页面. 编写相应的视图函数来处理密码丢失修改和密码修改逻辑. 编写发送验证信息到邮箱的逻辑. ...
- [oeasy]python0124_Code_page_437_IBM_5150_点阵式字形码_显示器效果
字符显示器 回忆上次内容 简体和繁体的汉字 字符数量都超级大 感谢王选和陈堃銶等前辈发明了激光照排技术 中文排版从此使用上了gb2312编码 添加图片注释,不超过 140 字(可选) ...
- el-date-picker 时间日期格式,选择范围限制
背景:ElementUI的 el-date-picker 使用时,有时候想要限制用户选择的时间范围,但是用的是datetimerange类型的选择器,不想单独写两个起始的选择器.我发现网上的方案大部分 ...
- 自写Json转换工具
前面写了简单的API测试工具ApiTools,返回的json有时需要做很多转换,于是开发了这个工具. 功能包括 1.json字符串转为表格,可以直观的展示,也可以复制,并支持转换后的表格点击列头进行排 ...
- 【DataBase】MySQL 13 分组查询
视频参考自:P59 - P68 https://www.bilibili.com/video/BV1xW411u7ax 分组查询 GROUP BY -- group by 子句 -- 要注意!grou ...
- 【Centos】RPM安装Mysql8
先去官网下载RPM包,没想到RPM包是红帽发行版 https://dev.mysql.com/downloads/mysql/ 使用wget直接下载到Centos里面: wget https://cd ...
- 【SqlServer】02 SSMS工具基本使用入门
之前的安装中除了SqlServer,还有一个SSMS管理工具 数据库的访问依赖于工具 SSMS提供了两种登陆方式: 创建用户: 删除用户: 创建数据库: 删除数据库: 创建表: 设置表的字段,字段名称 ...
- 感慨!美国人的大学课程搞的确实蛮好的,不是那种刻板读书的感觉 —— 课程:SERVICE-ORIENTED COMPUTING AND SYSTEM INTEGRATION: SOFTWARE, IOT, BIG DATA, AND AI AS SERVICES
无意间看到了一个美国人的课程,使用的教材:<SERVICE-ORIENTED COMPUTING AND SYSTEM INTEGRATION: SOFTWARE, IOT, BIG DATA, ...
- (续)使用MindSpore_hub 进行 加载模型用于推理或迁移学习
接前文: https://www.cnblogs.com/devilmaycry812839668/p/15005959.html ================================== ...