直接答案:25 个首页共 1,765 个 id 属性;10 站存在重复,合计 48 个重复名称、849 次多余出现。最集中的是 E03(233 次多余)、E22(154)、E06(149)、E14(120)、E23(105);E17 有 8 个重复名称但只多 10 次。页面里 label for 与 aria 引用共 4 个,全部能找到目标(0 悬空)。渲染后复测(15 站可渲染):12 站新增 id,E26 的重复从 2 个名称/17 次增至 4 个名称/33 次——JS 克隆会复制 id;10 站渲染失败单列。引用侧:4 个页面引用、0 悬空;183 处内联脚本取 id 中 1 处命中重复(E11 count-number)。
统计前先剥离 <script>、<style> 与注释,避免 JS 模板字符串里的 id="…" 把数字撑大;只统计静态 HTML。
方法:剥离脚本后统计静态 HTML 的 id
2026-11-09 13:41–13:42,对第 4 期 25 个缓存 200 首页实时请求 HTML;先去掉 <script>、<style> 与 HTML 注释,再用正则提取 id="…",按名称统计重复;同时收集 label for 与 aria-labelledby/describedby/controls/owns/activedescendant 引用的 id,检查是否在文档中存在。
| 口径 | 判定 | 边界 |
|---|---|---|
| id 属性 | 静态 HTML 中 id="…" 的出现次数 | 脚本、样式、注释内的字符串不计 |
| 重复名称 | 同名出现超过一次的名称数 | 大小写按原样比较 |
| 多余出现 | 每个重复名称的总次数减 1 后求和 | 不是“重复名称数” |
| 引用 | label for + aria idrefs 的 id | 只查存在性,不查顺序 |
| 悬空引用 | 引用目标在文档中不存在 | 重复目标仍算存在 |
结果:10 站有重复,少数站极重
| 指标 | 数量 | 站点数 |
|---|---|---|
| id 属性 | 1,765 | 24 |
| 重复名称 | 48 | 10 |
| 多余出现 | 849 | 10 |
| label / aria 引用 | 4 | 1 |
| 悬空引用 | 0 | 0 |
| 站点 | id 数 | 重复名称 | 多余出现 |
|---|---|---|---|
| E03 | 329 | 7 | 233 |
| E22 | 186 | 6 | 154 |
| E06 | 172 | 5 | 149 |
| E14 | 177 | 4 | 120 |
| E23 | 295 | 7 | 105 |
| E09 | 87 | 2 | 48 |
| E26 | 39 | 2 | 17 |
| E17 | 74 | 8 | 10 |
| E11 | 14 | 1 | 7 |
| E24 | 90 | 6 | 6 |
形态分两类:E03、E22、E06、E14、E23 是单一 id 被复制上百次,典型是轮播或卡片模板在循环里保留了固定 id;E17、E24 则是多个不同 id 各重复几次,更像组件在页面里出现了两次。前者更容易让 getElementById 与锚点命中错误元素。
渲染后再数一遍:15 站里 1 站新增重复
2026-11-09 14:41–14:45,用 headless Chrome(--dump-dom、虚拟时间 15 秒、每站独立 profile)对 25 站取渲染后 DOM,再用同一套口径统计 id;15 站成功渲染,10 站因 403/挑战页/超时被排除。
| 指标(15 个可渲染站) | 静态 | 渲染后 |
|---|---|---|
| id 总数 | 343 | 1,162 |
| 重复名称 | 11 | 13 |
| 多余出现 | 34 | 50 |
| 有重复的站点 | 3 | 3 |
| E26 的多余出现 | 17 | 33 |
12 个站渲染后 id 数量变化:E02 从 6 增至 644(全部唯一)、E25 7→133、E26 39→57、E18 1→17;E28 则从 1 变 0,说明脚本重写了 DOM。E26 是唯一新增重复的站:重复名称 2→4、多余出现 17→33,直接证明“JS 克隆组件会复制 id”;E11 与 E17 的重复在渲染前后一致,说明问题来自静态模板。E03、E06、E14、E22、E23、E24 等静态重复最重的站这次未能渲染,无法比较。
引用侧:4 个页面引用与 183 个脚本取 id,只有 1 个命中重复
重复 id 的影响取决于谁在引用它。2026-11-09 17:42–17:43 重新取同一批 25 个首页的静态 HTML:解析 label for、aria idrefs 与真实的 href="#id" 锚点,再扫描内联脚本里的 getElementById、querySelector('#id') 与 jQuery $('#id') 字面量。
| 引用类型 | 数量 | 指向重复 id | 悬空 |
|---|---|---|---|
| label for / aria idrefs | 4 | 0 | 0 |
| href="#id" 锚点 | 0 | 0 | 0 |
| 内联脚本取 id | 183(18 站) | 1 | — |
唯一命中是 E11:页面有 8 个 id="count-number",jQuery 选择 #count-number 时只拿到第一个 p.timer.count-title,其余 7 个不会更新;其余 182 处脚本取 id 都指向唯一名称。48 个重复名称里只有这 1 个被静态脚本引用,页面锚点为 0——风险是潜在的,不是已经大面积发生。
口径修正:onclick="this.href='#x'" 不是静态锚点,探针与站内 id 检查器都已排除;E04 的跳转链接由脚本生成,目标是 <a name>,不计入 id 引用。
对验收的启示
- 把 id 唯一性加入 HTML 校验。 10/25 站有重复;校验器通常一条规则就能发现。
- 克隆组件时重写 id。 E03 一个名称重复两百多次;模板循环里应追加索引或改用 class。
- 样式钩子用 class,不用 id。 只为加样式而写的 id 最容易在复制时冲突。
- 检查锚点与 aria 目标。 重复 id 会让页内跳转和 aria 引用指向第一个元素;本期引用少,但风险仍在。
- 在渲染后再查一次。 15 站复测里 E26 的重复由 2 个名称增至 4 个;静态唯一不代表运行时唯一,克隆组件后要再跑一遍。可用站内id 唯一性检查器在浏览器本地复查,企业官网验收可参考网站建设服务。
局限
- 只读静态 HTML:剥离 script/style/注释后统计,JS 运行时生成的 id 不计。
- 正则是近似:不构建 DOM,大小写按原样比较。
- 引用范围:页面查 label for、aria idrefs 与静态
href="#id";脚本只查内联getElementById/querySelector/jQuery 字面量,外链 bundle 与动态拼接不计。 - 悬空口径:重复 id 仍算“存在”,不判断引用是否指向正确元素。
- 脚本命中:只表示选择器会拿到第一个同名元素,不执行页面脚本,不判断第二个元素是否本该被选中。
- 渲染边界:15/25 站可渲染,10 站 403/挑战页/超时被排除;15 秒虚拟时间看不到更晚的异步插入。
- 25 站 pilot:同一批装备企业样本,不外推行业。
数据与复算
匿名数据:equipment-homepage-id-uniqueness-2026.csv(25 行 × 8 列:id 数、重复名称、多余出现、引用/悬空与时间)。仓库内为 docs/research/2026-11-homepage-id-uniqueness.csv。
渲染复测:equipment-homepage-id-runtime-2026.csv(25 行 × 14 列:渲染状态、静态/渲染后的 id、重复、多余出现与时间)。仓库内为 docs/research/2026-11-homepage-id-runtime.csv。
引用复测:equipment-homepage-id-references-2026.csv(25 行 × 16 列:页面引用、脚本取 id、重复命中与时间)。仓库内为 docs/research/2026-11-homepage-id-references.csv。
解析:scripts/id_uniqueness_probe.py;单元测试 scripts/test_content_id_uniqueness_probe.py(8 项,无网络);引用与脚本扫描见 scripts/test_content_id_reference_probe.py(13 项)。运行:
python -m unittest scripts.test_content_id_uniqueness_probe
配套:首页模板卫生审计、首页脚本卫生审计、首页语义、键盘与 ARIA。
数据来源与口径
- 样本:第 4 期 25 个缓存为 200 的首页。
- 抓取:2026-11-09 13:41–13:42 Asia/Shanghai,串行,站间 0.5 秒,超时 15 秒。
- 预处理:去掉 script、style 与注释后再提取 id。
- 引用:label for 与 aria idrefs;只查存在性,不查顺序。
常见问题
重复 id 影响大吗?
它是 HTML 校验错误,也会让 getElementById、页内锚点和 aria 目标只命中第一个元素;克隆组件时最容易踩到。
1,765 个 id 算多吗?
数量本身不是问题,唯一性才是。组件锚点、表单与 SVG 都会用 id;本期问题集中在少数站点的重复名称上。
0 个悬空引用是否说明没问题?
不能。本期只有 4 个 label/aria 引用,而且重复 id 会让引用指向错误元素——目标“存在”不等于指向正确。
JS 注入的 id 为什么不算?
本文先剥离 <script> 再统计静态 HTML;脚本运行时生成的 id 需要渲染后另测。
怎么修?
组件 id 加前缀或索引、克隆模板时重写 id;只需要样式钩子时用 class 或 data 属性,不要用 id。
数据能复核吗?
可以。公开 CSV 25 行 × 8 列;scripts/id_uniqueness_probe.py 有 8 项离线单测。
渲染后重复会变多吗?
会,但本次只有 1 站。15 个可渲染站里 E26 的重复名称从 2 增至 4、多余出现从 17 增至 33;其余站要么不变,要么新增的 id 全部唯一。
重复 id 会立刻让脚本出错吗?
不一定。25 站 183 处内联脚本取 id 中只有 1 处指向重复名称(E11 的 count-number,8 个同名);重复 id 是等待被引用的风险,不是必然故障。外链 bundle、动态拼接 id 和运行时插入不在本次扫描内。