如何让可折叠容器响应Transition属性?我的实现为何无平滑效果?
问题:实现Obsidian式平滑折叠容器失败,过渡效果生硬
我想做一个类似Obsidian Callouts的可点击折叠容器,核心要实现平滑展开/收起的过渡效果。通过控制台查看Obsidian的HTML,发现它是动态修改内容div的样式:
- 展开状态:
style="" - 收起状态:
style="display:none" - 展开过渡中:
style="overflow-y: clip; height: 98px; transition: all 100ms cubic-bezier(0.02, 0.01, 0.47, 1) 0s;" - 收起过渡中:
style="overflow-y: clip; height: 0px; transition: all 100ms cubic-bezier(0.02, 0.01, 0.47, 1) 0s;"
我发现它的高度会根据内容自动调整,于是用纯JS/CSS/HTML写了个简易版本,但切换时过渡效果还是很生硬,是不是我对Transition属性的理解有误?
我的代码如下:
代码片段
JavaScript
const Body = document.body const FoldableContainer = document.createElement("div") const FoldableHeader = document.createElement("div") const FoldableContent = document.createElement("div") FoldableHeader.innerHTML = "Clikc To Open" FoldableHeader.addEventListener("click", ()=>{ FoldableContainer.classList.toggle("open") if(FoldableContainer.classList.contains("open")){ FoldableContent.style.display = "inline" FoldableContent.style.height = "300px" }else{ FoldableContent.style.display = "none" FoldableContent.style.height = "0px" } }) FoldableContainer.className = "Container" FoldableContainer.style.height = "auto" FoldableContainer.style.padding = "50px" FoldableContainer.style.width = "200px" FoldableContainer.style.background = "#affaf7" FoldableContainer.appendChild(FoldableHeader) FoldableContent.innerHTML = "I am Text!. Infact im so much text that i make the Folable Container Div Larger!" FoldableContent.style.backgroundColor = "#8fbdbb" FoldableContent.style.transition = "all 1s" FoldableContent.style.display = "none" FoldableContent.style.height = "0px" FoldableContainer.appendChild(FoldableContent) Body.appendChild(FoldableContainer)
CSS
html { background-color: #FFFFFF; display: flex; justify-content: center; } div{ user-select: none; transition: all 1s; }
HTML
<!doctype html> <html> <head> <link rel ="stylesheet" href="styles.css"> </head> <body></body> <script src="script.js"></script> </html>
解决方案
你的问题核心在于错误使用了display: none——CSS过渡对display属性无效,一旦设置display: none,元素会立刻消失,过渡效果根本没机会触发。另外,硬写固定高度300px,也没法像Obsidian那样自适应内容高度。
修正思路:
- 抛弃
display: none,用height: 0配合overflow: hidden实现收起状态,让过渡能作用在height属性上。 - 展开时不要写固定高度,先获取内容的真实高度,再把这个值设为元素的
height,实现自适应。 - 收起时把
height设回0即可。
下面是修正后的代码:
修正后的JavaScript
const Body = document.body const FoldableContainer = document.createElement("div") const FoldableHeader = document.createElement("div") const FoldableContent = document.createElement("div") FoldableHeader.innerHTML = "Click To Open" FoldableHeader.addEventListener("click", ()=>{ FoldableContainer.classList.toggle("open") if(FoldableContainer.classList.contains("open")){ // 获取内容的真实高度(包括溢出部分) const contentHeight = FoldableContent.scrollHeight + "px" FoldableContent.style.height = contentHeight }else{ FoldableContent.style.height = "0px" } }) // 容器样式 FoldableContainer.className = "Container" FoldableContainer.style.padding = "50px" FoldableContainer.style.width = "200px" FoldableContainer.style.background = "#affaf7" FoldableContainer.appendChild(FoldableHeader) // 内容区域样式 FoldableContent.innerHTML = "I am Text! In fact I'm so much text that I make the Foldable Container Div Larger! 这里可以加更多测试文本,看看自适应效果如何..." FoldableContent.style.backgroundColor = "#8fbdbb" FoldableContent.style.transition = "height 0.3s cubic-bezier(0.02, 0.01, 0.47, 1)" FoldableContent.style.height = "0px" FoldableContent.style.overflow = "hidden" // 关键:隐藏溢出内容,避免高度为0时内容外露 FoldableContainer.appendChild(FoldableContent) Body.appendChild(FoldableContainer)
修正后的CSS(简化版)
html { background-color: #FFFFFF; display: flex; justify-content: center; padding-top: 50px; } div{ user-select: none; } .Container { transition: all 0.3s; }
额外说明
- 使用
scrollHeight获取元素的真实内容高度,确保展开时能完美适配内容长度。 - 把过渡属性明确指定为
height,比all更高效,避免不必要的属性过渡。 - 保留Obsidian的缓动函数
cubic-bezier(0.02, 0.01, 0.47, 1),让过渡效果更贴近原版。 - 收起时元素只是高度变为0,并没有消失,所以过渡能正常触发,实现平滑动画。
内容的提问来源于stack exchange,提问作者Eric Olsen
相关产品推荐
相关产品推荐

