Dawn 预测搜索:建议请求、键盘行为与结果页
本文基于 Dawn v15.3.0,源码固定到 ecb06c3(核验于 2026-09-29)。结论限于此提交,不表示其他版本行为相同;本篇为静态源码分析,未完成店铺运行验证。Dawn 上游已发布 v16.0.0(2026-08-10,GitHub Releases 核对),本库 Dawn 正文统一以 v15.3.0 为基线,升级前需重新核对。
对应哪些通用模式
主要对应站内搜索模块:搜索框、建议、结果页与无结果。建议面板是绝对定位的 div,由 <predictive-search> 的 open 属性与 CSS 显示;在固定提交的全仓库搜索中,popovertarget、popover=、showPopover、hidePopover 均未命中,所以它不是Popover所指的原生 Popover API 实现,只能在“紧贴输入框的非模态浮层”上类比。搜索入口本身在带 role="dialog" 的弹层里,属于模态形态。
入口与文件
| 文件 | 职责 | 何时输出 |
|---|---|---|
| snippets/header-search.liquid | details-modal 内的搜索弹层与输入框;开关决定包裹 <predictive-search> 还是 <search-form> | 页头 |
| sections/header.liquid | 渲染上述片段:top-center 布局或未选菜单时多渲染一份(Search-In-Modal-1),另一份 Search-In-Modal,CSS 按断点显示其一 | 页头 |
| sections/predictive-search.liquid | 无 schema,只作建议请求的返回体:role="listbox" 结果、“Search for”选项、计数文本 | 仅建议响应 |
| assets/predictive-search.js、search-form.js | 请求、渲染、键盘、状态;后者提供 300ms 防抖与清除按钮 | 开关为真才加载前者 |
| sections/main-search.liquid、main-search.js | 结果页搜索框、计数、paginate 每页 24、筛选与排序(复用 facets 片段) | templates/search.json |
layout/theme.liquid 在 head 加载 search-form.js,文末按 predictive_search_enabled 加载 predictive-search.js,并写入 window.routes.predictive_search_url。
数据从哪来
- 请求:
fetch请求routes.predictive_search_url(Liquid 路由值,通用页记该端点为/search/suggest)加查询串?q=<编码词>§ion_id=predictive-search。只传q与section_id,不传resources[type]、resources[limit]、resources[options][unavailable_products],类型、数量与缺货处理走平台默认与 Search & Discovery 设置(默认值见站内搜索模块「映射到 Shopify」)。 - Liquid 对象:
predictive_search.performed、.terms、.resources.queries / collections / pages / articles / products;查询建议用query.styled_text,价格经price片段。 - 商家设置(config/settings_schema.json「Search behavior」):
predictive_search_enabled(默认 true)、predictive_search_show_vendor、predictive_search_show_price(默认 false)。
交互怎样运行
- 输入经 300ms 防抖调用
onChange;不再相关的旧结果被移除,空词则关闭并清空。 getSearchResults先查内存缓存(键为小写词,仅替换第一个空格);未命中则请求,取响应里#shopify-section-predictive-search的innerHTML,并同步写入页面全部实例的缓存。- 渲染后写
open、aria-expanded="true",按视口与页头底部算面板max-height;请求期间设loading属性,CSS 据此显示旋转图标。 - 键盘:
keyup的上下键在可见li与“Search for”按钮间循环,写aria-selected与aria-activedescendant;Enter 点击选中项;keydown阻止上下键移动光标。焦点始终在输入框。 - 焦点离开后异步关闭;重置按钮清空输入、中止请求并关闭。
- 无 JS 时它是指向搜索页的 GET 表单(隐藏字段
options[prefix]=last),建议只是增强。
与通用契约的一致与差距
| 契约(站内搜索) | Dawn v15.3.0 | 判断 |
|---|---|---|
| combobox 结构与焦点 | 输入框有 role="combobox"、aria-expanded、aria-controls、aria-autocomplete="list"、aria-haspopup="listbox";选项 role="option",链接 tabindex="-1",焦点留在输入框 | 一致;aria-owns 与 aria-controls 指向同一 id,实际朗读未核验 |
| listbox 子结构 | 分组用 ul role="group",但 h2 与 div 容器也在 listbox 内 | 是否兼容 APG 预期,未核验 |
| Escape | predictive-search.js 未处理;页头由外层 details-modal 在 keyup 关闭整个弹层并把焦点还给触发按钮,结果页搜索框无对应处理 | 差距:没有“先关建议、再清输入” |
| 不自动选中、Enter | 打开时无选中项;有选中链接则阻止提交并点击,否则原生提交原词 | 一致;Home、End、Alt+方向键未处理(契约中为可选) |
| 结果数量通知 | 视觉隐藏的 role="status" 节点先写“加载中”,再写隐藏节点里的计数文案,1 秒后设 aria-hidden="true";结果页另有 <p role="status"> | 有实现;aria-hidden 切换的播报效果未核验 |
| 建议为空 | 面板仍显示“Search for”选项,状态文本读无结果文案 | 与“收起面板”不同 |
| 取消过期请求 | AbortController 只在重置按钮时中止;连续输入不取消旧请求,也不校验响应是否过期 | 差距:乱序响应可能覆盖新结果(静态推断,未复现) |
| 请求失败 | 非 2xx 或异常时 close() 并抛错,无可见提示与重试,429 同样处理 | 差距:失败表现为面板消失 |
| 无结果页 | <p role="status"> 回显词并提示检查拼写;已读的 main-search.liquid 中未见热门分类或客服入口 | 部分一致 |
| 拼写容错回显 | 全仓库搜索未找到(搜索词:spell、did you mean、showing results for、corrected;仅命中 spellcheck="false" 与无结果文案) | 主题未内置;平台是否提供字段未核验 |
| 同页多个搜索 | 页头至多两份,结果页再一份;结果标记里的 predictive-search-results 等 id 在多实例同时渲染时会重复 | 风险,是否同时渲染未核验 |
定制入口与风险
商家侧只有上述三个主题设置。开发侧耦合点:predictive-search-results、predictive-search-option-* 与 #shopify-section-predictive-search 被 JS 硬编码依赖;switchOption 依赖 li 与 button.predictive-search__item;页面、文章列表在桌面与移动各输出一份并靠 CSS 隐藏;sticky-header 在建议打开时忽略滚动。改标记或加入 resources[...] 参数时需同步检查。
建议的验证范围
本篇均未执行:页头与结果页分别输入英文、中文词,核对 /search/suggest 的请求与响应;限速下连续输入,确认乱序;断网与模拟 429,确认面板与状态文本;只用键盘完成输入、选择、提交与 Escape;用读屏软件核对计数播报;top-center 布局下检查重复 id。