纯:hover在大促页不可用,因移动端失效、键盘用户无法操作、屏幕阅读器无状态反馈,且违反WCAG 2.1;必须用nav+ul+aria-expanded语义结构,配合focusin/keydown事件及ARIA属性实现三端兼容。

悬挂式导航(即悬停展开的下拉菜单)在大促专题页里很常见,但直接套用:hover就翻车——移动端不响应、键盘用户无法操作、屏幕阅读器读不出状态。必须兼顾鼠标、触控、键盘三类交互,且不能牺牲语义和可访问性。
为什么纯 :hover 在大促页里根本不能用
大促专题页流量入口多(微信内嵌、APP WebView、短信跳转),设备类型杂,:hover 仅触发于悬停,对触摸屏完全失效;更严重的是,它绕过了焦点管理,Tab 键用户卡在父项无法进入子菜单,违反 WCAG 2.1 标准。
- iOS Safari 和 Android Chrome 的“伪悬停”行为不可靠:第一次点击触发
:hover,第二次才跳转,造成误操作 - 无障碍测试工具(如 axe)会直接报错:
aria-expanded缺失、role="menu"未声明 - 搜索引擎抓取时忽略
:hover下拉内容,促销链接的 SEO 权重归零
nav + ul + aria-expanded 是唯一合规起点
结构必须用 <nav aria-label="大促活动导航"></nav> 包裹,子菜单用 <ul role="menu"></ul>,每个下拉触发项(如“满减专区”)用 <button aria-haspopup="menu" aria-expanded="false"></button>,不能是 <a href="#"></a> —— 后者默认可跳转,会干扰 JS 控制逻辑。
- 子菜单容器加
id="promo-dropdown-1",对应按钮的aria-controls="promo-dropdown-1" - 初始状态设
aria-hidden="true",展开时同步切为"false",不能只靠display: none - 禁用
outline: none,改用box-shadow: 0 0 0 2px #007bff保证焦点可见
JavaScript 必须监听 focusin 和 keydown,不只是 click
只绑 click 事件,等于放弃键盘用户。大促页常有中老年用户依赖键盘导航,必须支持方向键切换、Enter 展开、Esc 关闭。
立即学习“前端免费学习笔记(深入)”;
- 给触发按钮监听
focusin:获得焦点时自动展开菜单(模拟悬停意图) - 监听
keydown:捕获ArrowDown进入子项、Escape收起、Tab移出时自动关闭 - 子菜单项用
role="menuitem",并确保tabindex="-1",避免 Tab 键跳过中间项 - 示例关键逻辑:
document.querySelectorAll('[aria-haspopup="menu"]').forEach(trigger => {
trigger.addEventListener('click', e => {
e.preventDefault();
const expanded = trigger.getAttribute('aria-expanded') === 'true';
trigger.setAttribute('aria-expanded', !expanded);
const menu = document.getElementById(trigger.getAttribute('aria-controls'));
menu.setAttribute('aria-hidden', expanded);
});
trigger.addEventListener('focusin', () => {
trigger.setAttribute('aria-expanded', 'true');
const menu = document.getElementById(trigger.getAttribute('aria-controls'));
menu.setAttribute('aria-hidden', 'false');
});
});
移动端折叠后,transform 比 display 更稳
大促页常需在小屏收起整个导航栏,用 display: none 会导致 CSS 动画中断、Vue/React 组件卸载重绘卡顿;而 transform: translateX(-100%) 保留 DOM 结构,动画流畅,且 Safari iOS 不会闪屏。
- 汉堡图标本身用
<button aria-label="打开活动导航"></button>,别用<div> 或空 <code><a></a> - 收起状态加
inert属性(现代浏览器支持),或手动设tabindex="-1"阻止焦点穿透 - 媒体查询别只写
@media (max-width: 768px),补上screen and (max-width: 768px)避免部分安卓 WebView 忽略 - 注意:iOS Safari 对
position: fixed+transform组合有渲染 bug,此时换用position: sticky更可靠
最易被忽略的点是:大促页往往用 CDN 加速静态资源,但 ARIA 属性和焦点逻辑必须随 HTML 一起输出,不能全靠 JS 异步注入——首屏加载完成前,键盘用户已开始 Tab 导航,JS 还没执行完就卡死。



















