应使用opacity和transform实现平滑显隐切换,避免display;需用布尔变量控制状态、语义化嵌套结构、添加touch-action提升移动端点击响应。

用opacity + transform做显隐切换,别碰display
直接切display: none会触发重排,卡片“啪”一下闪现或跳动,根本谈不上平滑。浏览器对display变化不做过渡插值,CSS的transition对它完全无效。
真正能硬件加速、不卡顿的组合只有两个属性:opacity和transform(比如translateY或scale)。它们不改变布局,只影响绘制层,GPU能直接接管。
- 给所有标签页内容区统一设
opacity: 0; transform: translateY(10px);,再加transition: opacity 0.3s ease, transform 0.3s ease; - 激活态加
opacity: 1; transform: translateY(0); - 必须确保父容器有
contain: layout style paint,否则滚动或动画时仍可能意外重排
状态锁比transitionend更可靠
用户快速连点标签时,transitionend事件容易堆积或丢失,导致UI残留旧状态、新内容没显示,甚至两页同时可见。
与其依赖事件回调清理状态,不如用一个简单布尔变量做开关:
立即学习“前端免费学习笔记(深入)”;
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
- 点击前先检查
isTransitioning是否为true,是则直接返回 - 设为
true后立即更新DOM类名,再启动过渡 - 在
transitionend里设回false,但仅用于收尾,不用于判断是否可开始下一次切换
卡片结构要嵌套,不能平级堆div
如果把每个标签页内容写成同级<div id="tab1"></div>
<div id="tab2"></div>,再靠JS切display或opacity,视觉上看似切换了,但实际存在两个隐患:一是焦点管理混乱,键盘Tab会穿进隐藏页;二是无障碍属性aria-hidden无法精准同步。
正确做法是让每个页签和对应面板形成语义闭环:
- 页签用
<button role="tab" aria-controls="panel1" aria-selected="true"></button> - 面板用
<div id="panel1" role="tabpanel" aria-labelledby="tab1"> <li>所有面板包在一个<code>role="tablist"容器内,且初始只有一组aria-selected="true"和aria-hidden="false"
移动端点击区域小?补个touch-action
在iOS Safari或部分Android WebView里,卡片标签点击无响应,不是JS没绑上,而是浏览器把touchstart当成了页面缩放手势——尤其当标签文字少、padding小的时候。
最轻量的解法是在标签容器上加一行CSS:
tab-header {
touch-action: manipulation;
}
这告诉浏览器:“这里只做点击/滑动,别抢事件”。比手动preventDefault()更安全,也不影响内部链接或表单操作。
真正难搞的不是怎么动起来,而是动完之后状态还对不对——尤其是快速切换+键盘导航+屏幕阅读器三者叠加时,aria-selected和tabindex稍一不同步,整个组件就不可用了。


















