学习前提
理解button、链接、DOM事件与焦点;掌握hidden属性的用途。
本课完成后你能做什么
实现先展开选项、再选择语言的两步交互,并处理关闭与键盘操作。
先理解机制
触发按钮控制选项可见性,语言链接负责导航,两种职责分开。点击按钮只更新展开状态,不修改URL或页面语言。aria-expanded必须与hidden同步,aria-controls指向真实存在的选项容器。
这是包含普通链接的disclosure,不必添加复杂menu角色。原生按钮支持Enter和空格,链接可以用Tab访问。Escape关闭并把焦点还给触发器;点击外部或焦点离开区域也关闭。
真实网站应在生成目标URL时保留文章id、搜索参数和片段,只替换lang参数。本示例用简化URL演示交互,页面本身没有翻译引擎;需接入已有双语内容才能改变正文。
可运行示例
<div id="picker">
<button id="trigger" type="button" aria-expanded="false" aria-controls="choices">Language ▾</button>
<div id="choices" hidden><a href="?lang=zh">中文</a> <a href="?lang=en">English</a></div>
</div>
<script>
const root = document.querySelector('#picker');
const trigger = document.querySelector('#trigger');
const choices = document.querySelector('#choices');
function setOpen(open) { choices.hidden = !open; trigger.setAttribute('aria-expanded', String(open)); }
trigger.addEventListener('click', () => setOpen(choices.hidden));
document.addEventListener('click', event => { if (!root.contains(event.target)) setOpen(false); });
root.addEventListener('keydown', event => {
if (event.key === 'Escape') { setOpen(false); trigger.focus(); }
});
root.addEventListener('focusout', event => {
if (event.relatedTarget && !root.contains(event.relatedTarget)) setOpen(false);
});
</script>
推演与验证
第一次点击后URL不变且两项可见;第二次点击关闭。再次展开,Tab进入选项;Escape关闭并返回按钮。点击外部以及Tab离开区域都应关闭。选择链接才允许导航。
常见错误与边界
不要让mouseenter触发导航,也不要在按钮点击时同时切换语言。display:none或hidden会移除隐藏链接的键盘路径,单纯opacity:0不会。
动手练习
用URL与searchParams从当前位置构造两个链接,保留hash;给当前语言添加aria-current。
参考解法与理由
创建new URL(location.href),对副本设置lang再赋给链接href。只修改lang可保留其他参数;当前语言用aria-current=true,不要把选项写成没有行为的span。