抽屉导航的dom结构应将抽屉容器紧邻body、设唯一id并用aria-hidden/inert控制可访问性,切换按钮需关联aria-expanded,动画必须用transform且配合body滚动冻结与touch-action优化。

抽屉导航的 DOM 结构怎么组织才不影响可访问性
侧边抽屉必须能被键盘和屏幕阅读器识别,不能只靠 display: none 或 visibility: hidden 隐藏。推荐用 aria-hidden="true" 控制语义可见性,配合 inert 属性(现代浏览器支持)或手动移除 tabindex 和禁用表单聚焦。
结构上,抽屉容器应紧邻 ,避免嵌套在布局组件内导致 z-index 冲突或 focus trap 失效:
<nav id="drawer" aria-hidden="true" inert><ul><li><a href="/home">首页</a></li></ul></nav><main>...</main><button id="drawer-toggle">☰</button>
- 抽屉元素必须有唯一
id,供aria-controls关联触发按钮 - 切换按钮需带
aria-expanded="false",打开时改为true - 不要把抽屉放在
<header></header>或<aside></aside>内部——它们可能被 CSS containment 截断动画或焦点流
CSS 过渡动画该用 transform 还是 left/top
用 transform: translateX() 是唯一可靠选择。直接改 left 会触发重排,且在移动端 Safari 上常出现闪烁或卡顿;opacity 单独过渡无法解决遮罩层穿透点击问题。
关键样式要锁定 will-change: transform 并启用硬件加速:
#drawer {
position: fixed;
top: 0;
left: 0;
width: 280px;
height: 100vh;
background: #fff;
transform: translateX(-100%);
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
will-change: transform;
}
- 过渡时间别设成
0.5s以上——用户感知延迟明显,尤其安卓低端机 -
cubic-bezier(0.34, 1.56, 0.64, 1)比ease-out更自然,收尾不拖沓 - 必须同时设置
overflow-x: hidden在上,否则抽屉滑出时页面仍可水平滚动
JavaScript 切换状态时如何避免滚动穿透
抽屉展开时,背景内容默认还能滚动,这是最常被忽略的交互缺陷。不能只靠 overflow: hidden —— iOS Safari 会保留当前滚动位置,松手后突然回弹。
正确做法是记录并冻结 body 的 scrollTop,再用内联样式锁定:
let scrollY = 0;
function openDrawer() {
scrollY = window.scrollY;
document.body.style.position = 'fixed';
document.body.style.top = `-${scrollY}px`;
document.body.setAttribute('inert', '');
drawer.setAttribute('aria-hidden', 'false');
drawer.removeAttribute('inert');
}
- 关闭时要恢复
document.body.style并重置scrollTop,否则页面跳到顶部 - 用
inert属性比遍历所有子元素加pointer-events: none更稳妥,兼容性可通过 polyfill 补齐 - 若项目已用 React/Vue,注意事件委托失效问题——抽屉内动态渲染的按钮需重新绑定事件
移动端 Safari 的 touchmove 阻断和点击穿透怎么解
iOS Safari 在抽屉滑动过程中,手指离开区域后仍会触发底层元素的 click,尤其在导航项密集时极易误触。这不是 bug,是它对 passive event listener 的强制策略。
解决方案分两层:
- 给
#drawer加touch-action: pan-y,允许垂直滚动但禁止水平拖拽干扰 - 抽屉 overlay 遮罩层必须用
position: fixed+inset: 0,且监听touchstart立即preventDefault(),而不是等click - 避免在抽屉里放
input或textarea—— iOS 键盘弹出会强制重绘,导致动画撕裂;真需要输入,用 modal 取代抽屉
动画结束时机也得小心:transitionend 在快速连续开关时可能漏触发,建议用 setTimeout 做 fallback,但延时别超过 320ms(人眼可感知延迟上限)。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











