学习前提

理解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。

进一步阅读