HTML5高级实战:构建可访问的现代Web组件库
一、架构设计原则
基于语义化HTML5+WAI-ARIA+键盘导航的组件系统,满足WCAG 2.1 AA级可访问性标准
二、核心组件实现
1. 无障碍对话框
<!-- 对话框组件 -->
<div class="modal" role="dialog" aria-modal="true"
aria-labelledby="modal-title" aria-describedby="modal-desc">
<div class="modal-content">
<h2 id="modal-title">确认操作</h2>
<p id="modal-desc">您确定要删除此项吗?此操作不可撤销。</p>
<div class="modal-actions">
<button aria-label="确认删除" onclick="handleConfirm()">
确认
</button>
<button aria-label="取消操作" onclick="closeModal()"
autofocus>
取消
</button>
</div>
</div>
</div>
<script>
// 焦点管理
function openModal() {
document.querySelector('.modal').style.display = 'block';
document.querySelector('.modal [autofocus]').focus();
// 禁用背景内容
document.querySelector('main').setAttribute('aria-hidden', 'true');
}
function closeModal() {
document.querySelector('.modal').style.display = 'none';
// 恢复背景内容
document.querySelector('main').removeAttribute('aria-hidden');
}
</script>
2. 标签页组件
<div class="tablist" role="tablist" aria-label="用户设置">
<button role="tab"
aria-selected="true"
aria-controls="profile-panel"
id="profile-tab"
tabindex="0">
个人资料
</button>
<button role="tab"
aria-selected="false"
aria-controls="security-panel"
id="security-tab"
tabindex="-1">
安全设置
</button>
</div>
<div id="profile-panel"
role="tabpanel"
aria-labelledby="profile-tab"
tabindex="0">
<!-- 个人资料内容 -->
</div>
<div id="security-panel"
role="tabpanel"
aria-labelledby="security-tab"
tabindex="0"
hidden>
<!-- 安全设置内容 -->
</div>
<script>
// 标签页切换逻辑
document.querySelectorAll('[role="tab"]').forEach(tab => {
tab.addEventListener('click', () => {
// 更新ARIA状态
document.querySelectorAll('[role="tab"]').forEach(t => {
t.setAttribute('aria-selected', 'false');
t.setAttribute('tabindex', '-1');
});
tab.setAttribute('aria-selected', 'true');
tab.setAttribute('tabindex', '0');
// 显示对应面板
const panelId = tab.getAttribute('aria-controls');
document.querySelectorAll('[role="tabpanel"]').forEach(p => {
p.hidden = true;
});
document.getElementById(panelId).hidden = false;
});
// 键盘导航支持
tab.addEventListener('keydown', e => {
if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
const tabs = Array.from(document.querySelectorAll('[role="tab"]'));
const currentIndex = tabs.indexOf(tab);
const direction = e.key === 'ArrowLeft' ? -1 : 1;
const newIndex = (currentIndex + direction + tabs.length) % tabs.length;
tabs[newIndex].click();
tabs[newIndex].focus();
}
});
});
</script>
三、最佳实践方案
- 焦点管理:确保键盘操作的逻辑顺序
- ARIA实时区域:动态内容更新通知屏幕阅读器
- 颜色对比:文本与背景至少4.5:1的对比度
- 语义化标记:正确使用HTML5结构元素