搜索框是绝大多数网站的核心组件,但长期以来我们习惯使用<div>或<form>来包裹搜索区域。这样不仅缺乏语义,也无法让辅助技术准确识别搜索地标。HTML新引入的<search>元素专门用于标记搜索表单及其相关控件,为浏览器和屏幕阅读器提供明确的搜索语义。本文将结合自动完成、实时建议和ARIA增强,构建一个生产级的语义搜索模块。
一、search元素解决了什么问题?
在<search>出现之前,开发者通常会这样标记搜索区域:
<div role="search">
<form>
<input type="search" aria-label="站内搜索">
<button>搜索</button>
</form>
</div>
这需要手动添加role="search"和aria-label才能让屏幕阅读器将其识别为搜索地标。而<search>元素本身就隐式拥有role="search",并且浏览器会为其提供与导航、主要内容等相似的地标暴露级别,无需额外ARIA属性。
更重要的是,<search>可以包含多个表单或任何搜索相关的交互控件(如筛选、排序),而不仅仅是单一输入框。它标识的是“搜索区域”这一语义概念,而非具体的表单行为。
二、基础案例:一个简单的站点搜索
让我们从一个最简示例开始,展示<search>的基本结构:
<search>
<form action="/search" method="get">
<input type="search" name="q" placeholder="搜索文章...">
<button type="submit">🔍</button>
</form>
</search>
该代码被屏幕阅读器解析时,会自动提示“搜索区域”。使用Ctrl+F5或在辅助技术的区域导航中,用户可直接跳转到搜索地标。无需任何role属性,语义由元素本身提供。
注意:<search>不能嵌套在另一个<search>内部。一个页面可以有多个搜索区域(例如全局搜索和页面内筛选搜索),每个都应使用独立的<search>。
三、进阶实战:带自动完成建议的搜索模块
真实应用中,搜索通常伴随下拉建议列表。我们将构建一个包含输入框、提交按钮和动态建议面板的完整模块,并确保键盘导航和辅助技术都能正确处理。
3.1 HTML结构
<search aria-label="全站搜索">
<form role="search" onsubmit="return false">
<div class="search-wrapper">
<input type="search" id="search-input"
autocomplete="off"
aria-expanded="false"
aria-controls="suggestions-list"
aria-activedescendant=""
aria-describedby="search-help">
<button type="submit" aria-label="执行搜索">🔍</button>
<span id="search-help" hidden>输入关键词后,使用上下箭头选择建议</span>
</div>
<ul id="suggestions-list" role="listbox" aria-label="搜索建议">
<!-- 动态填充 -->
</ul>
</form>
</search>
关键无障碍设计:
aria-expanded指示下拉列表打开/关闭状态。aria-controls绑定建议列表。aria-activedescendant指向当前高亮建议的ID,避免焦点在输入框和建议之间移动,适合组合框模式。role="listbox"和对应的option角色明确建议列表性质。
3.2 动态建议的JavaScript逻辑
const input = document.getElementById('search-input');
const list = document.getElementById('suggestions-list');
let activeIndex = -1;
// 模拟建议数据
const allSuggestions = ['HTML search', 'HTML dialog', 'HTML popover', 'HTML inert', 'HTML canvas'];
input.addEventListener('input', () => {
const val = input.value.trim().toLowerCase();
const filtered = val ? allSuggestions.filter(s => s.toLowerCase().includes(val)) : [];
updateList(filtered);
activeIndex = -1;
updateActiveDescendant();
});
function updateList(items) {
list.innerHTML = '';
items.forEach((item, idx) => {
const li = document.createElement('li');
li.id = `suggestion-${idx}`;
li.role = 'option';
li.textContent = item;
li.addEventListener('click', () => selectItem(item));
list.appendChild(li);
});
input.setAttribute('aria-expanded', items.length > 0);
}
input.addEventListener('keydown', (e) => {
const items = list.querySelectorAll('li');
if (e.key === 'ArrowDown') {
e.preventDefault();
activeIndex = Math.min(activeIndex + 1, items.length - 1);
updateActiveDescendant();
items[activeIndex]?.scrollIntoView({ block: 'nearest' });
} else if (e.key === 'ArrowUp') {
e.preventDefault();
activeIndex = Math.max(activeIndex - 1, 0);
updateActiveDescendant();
items[activeIndex]?.scrollIntoView({ block: 'nearest' });
} else if (e.key === 'Enter' && activeIndex >= 0) {
e.preventDefault();
const selected = items[activeIndex]?.textContent;
if (selected) selectItem(selected);
} else if (e.key === 'Escape') {
list.innerHTML = '';
input.setAttribute('aria-expanded', 'false');
activeIndex = -1;
}
});
function updateActiveDescendant() {
const items = list.querySelectorAll('li');
items.forEach((li, i) => {
li.classList.toggle('active', i === activeIndex);
});
input.setAttribute('aria-activedescendant', activeIndex >= 0 ? `suggestion-${activeIndex}` : '');
}
function selectItem(text) {
input.value = text;
list.innerHTML = '';
input.setAttribute('aria-expanded', 'false');
activeIndex = -1;
// 执行搜索动作
window.location.href = `/search?q=${encodeURIComponent(text)}`;
}
通过该脚本,我们实现了一个完全符合ARIA组合框模式的搜索建议组件,配合<search>元素,地标语义和控件语义达到现代标准。
四、与role=”search”的兼容性处理
虽然主流浏览器(Chrome 121+、Edge 121+、Firefox 122+、Safari 17+)均已支持<search>,但为了兼容更早的浏览器,可以使用特性检测并回退:
<!-- 回退方案:使用div并添加role -->
<div role="search">
<search>
<form>...</form>
</search>
</div>
或通过CSS @supports 提供样式:
@supports not (selector(:search)) {
search { display: block; }
}
但通常不需要,因为<search>本身在旧浏览器中会被当作div渲染,只是没有默认地标角色,仍可正常使用。为安全起见,可以在<search>上额外添加role="search",新型浏览器会忽略因为隐式已有,旧浏览器则依靠显式角色。
五、其他应用场景与最佳实践
除了全局搜索,<search>还适用于:
- 筛选面板:例如电商产品列表的筛选条件区域,包含多个
<select>和<input>。 - 高级搜索页面:复杂查询表单,可包含多个字段。
- 文章内搜索:浏览器内置页面查找之外的应用内文章搜索。
书写规则:
- 每个
<search>应有清晰的上下文,必要时添加aria-label(例如页面有多个搜索区域时)。 - 内部应至少包含一个
<form>或交互控件,但绝不是装饰性的。 - 避免将非搜索相关的控件放入
<search>,以免误导地标导航。
六、可访问性测试与验证
使用NVDA或VoiceOver打开页面,按D键(地标导航)应能听到“搜索”地标。通过Tab进入输入框,屏幕阅读器会读出aria-describedby指定的帮助文本。下拉建议通过aria-activedescendant自动播报高亮项,无需焦点移动,用户体验流畅且符合认知。
可以使用Lighthouse或axe DevTools进行自动化审计,确认搜索区域的可访问性得分。
七、总结
<search>元素的引入,填补了HTML语义化中搜索区域的长期空白。搭配ARIA属性,我们能用更少的标记实现更精准的无障碍地标和控件语义。本文实现的自动完成搜索模块,不仅代码简洁,且在键盘导航和屏幕阅读器表现上达到了专业水准。现在,用<search>替换你项目中所有<div role="search">吧,让信息检索的入口更加标准、包容和未来友好。

