id重复的页面占六成,浏览器只认第一个,而它多半看不见

id重复的页面占六成,浏览器只认第一个,而它多半看不见
张文保 29 分钟阅读 2,746 阅读
本文目录
  1. 一页上的id,本来该管什么用?
  2. 这一轮量了什么,为什么要先把SVG和空名字挑出去?
  3. 六成页面撞了名,撞的是些什么?
  4. 中位数是2,最大值是314
  5. 撞上的那份,为什么七成八当场看不见?
  6. 为什么偏偏是第一个
  7. 第一个看不见的时候,页面会怎么样?
  8. 同一个组件渲染两遍,和两个不相干的东西撞名,哪种更难查?
  9. 不相干撞名才是会咬人的那一类
  10. 商品页为什么比首页糟一倍?
  11. 加入购物车那个按钮,同名的有十四个
  12. 尺码、订阅、收藏,撞的都是转化链上的那几格
  13. 这些名字是谁起的?
  14. 短数字结尾那一类最容易出事
  15. 这些名字有一半是机器拼出来的
  16. 只curl一次能看见多少?读三遍呢?换成手机呢?
  17. 照着改,先动哪几处?
  18. 先改被指认的,再改没人指认的
  19. 常见问题解答
  20. 一个页面上有重复的id,会影响SEO排名吗?
  21. 怎么快速判断本页有没有重复id?
  22. 重复的id会让页面报错吗?
  23. 第一个同名元素是隐藏的,锚点跳转会怎样?
  24. id里可以有空格或者以数字开头吗?
  25. 为什么商品页的重复id比首页多这么多?
  26. 内联SVG图标里的重复id要不要管?
  27. 权威参考资料

摘要:131个电商站的181个页面,四万多个带id的元素里有1508组名字撞了车,64.6%的页面至少有一处,62.2%的站至少有一页有。撞名之后没有任何报错,浏览器只是安静地把标签关联、锚点跳转、脚本取元素这三件事全部落到文档里第一个同名元素身上——而这批数据里,第一个当场看不见的占78.2%。真浏览器把后果跑了一遍:第一个是display:none就完全不动,是visibility:hidden就滚过去一片空白,在折叠面板里浏览器反倒会替你展开。

一个页面上的元素之间是互相认识的。这一格输入框的说明文字是哪一段、这个链接要跳到哪一块、这段脚本要改的是谁,全靠一样东西串起来:名字。写在HTML里就是id。

名字这套机制有个前提,前提写在规范里,也写在每一个前端新人第一周就被告知的常识里——一页之内不能重名。但常识归常识,页面是拼出来的:主题给一份、插件给一份、营销部门临时插的代码片段再给一份,谁也不知道别人用了什么名字。

这件事跟另一种重复很像但不是一回事。重复的meta标签那篇量的是页头里同一件事被声明了两遍、机器各按各的规矩挑一条,那是声明层的事。这一篇量的是正文里元素之间的指认关系——名字撞了,指认动作照样成立,只是永远落在同一个元素上。

所以真实的页面到底重不重?重了之后会怎么样?这两个问题以前只能靠经验回答。文章目录怎么挂锚点那篇给过一套锚ID的命名规范,还留了一行自查用的JavaScript,但那是方法论,没有数字。这次把那行自查跑到了131个大站的181个页面上。

一页上的id,本来该管什么用?

先把这件事的边界说清楚,因为它比大多数人以为的窄,也比大多数人以为的深。

HTML规范里id属性那一节对这个值只提了三条要求:不能是空字符串、不能含有空白字符、在整个文档里必须唯一。注意第三条的措辞——它是对写页面的人提的要求,不是对浏览器提的。规范并没有说浏览器遇到重名要报错、要拒绝渲染、要挑一个丢一个。它什么都不说。

浏览器的处理写在另一份规范里。DOM规范对getElementById的定义是:返回文档中第一个id等于给定值的元素。这个第一个指的是树序,也就是HTML源码里从上往下数的第一个。

顺着这条定义,一整串动作都被绑在了同一个元素上:

  • 脚本取元素,取到第一个;
  • 标签的for属性关联控件,关联第一个;
  • 地址栏里的井号跳转,跳到第一个;
  • 无障碍树算元素叫什么名字的时候,可及名称计算规范里aria-labelledby那一步取的也是第一个;
  • 输入框用form属性挂到某张表单上,挂到第一个。

只有一个东西不听这套:CSS。选择器里的井号写法在样式引擎眼里就是一个属性匹配,重名的两个元素样式都会生效。这个不对称是整件事最阴的地方——看得见的那一层对两个都生效,操作的那一层只认第一个,于是页面看上去完全正常,问题只在有人去点、去跳、去读的那一刻才出现。

还有个细节值得先摆出来。无障碍标准里原本有一条成功准则专门管这个,编号4.1.1,名字叫解析,要求包括id唯一在内的标记合法性。2023年的WCAG 2.2把4.1.1整条标成了已废除,理由是现代浏览器和辅助技术早就自己处理了这类问题,这条准则产出的多是无意义的失败判定。

废掉的是合规判定,不是后果。MDN关于id全局属性的说明把四个使用场景列得很清楚:锚点、标签关联、脚本取值、CSS选择器,前三个都只认第一个,只有最后一个例外。这一点下面会用实测数据说清楚。

这一轮量了什么,为什么要先把SVG和空名字挑出去?

取的是131个站,每站首页一份,其中56个站另取一个商品页,一共187个页面。真浏览器打开、等三秒半、滚到底再滚回顶、再等一下才读,读的是渲染完的DOM,不是服务端吐出来的那份HTML。最后200且解析成功的181个页面,来自127个站。取不回的六个:403三个、404两个、加载报错一个。

读法很直接:把每个页面上所有带id的元素列出来,按名字分组,组里超过一个的就是撞名。加上每个元素的标签名、当场可不可见、以及往上四层的祖先链,用来判断这两个同名的东西是不是同一个组件的两次渲染。

第一遍跑完,最常撞的名字排行榜上前几名长这样:Vector、Vector_2、Layer_1、clip0_3601_92。这些不是谁起的烂名字,是设计师从Figma里导出SVG图标时软件自动生成的图层名。一个页面里放二十个内联图标,二十份Vector就都进来了。

如果把它们算进去,这篇文章的结论会变成一句废话:撞名的都是SVG图层。所以口径必须先拆。

类别撞名组数涉及元素怎么判定本文口径
SVG内部标识3972007组里全是SVG专属标签,或祖先链里有svg另算
空名字1001291id写成了空字符串另算
页面结构id15084672剩下的全部本文只算这一类

SVG内部那一档单独看也有意思:397组分布在76个页面、50个站上,Vector和Layer_1各在7个站里撞过。它们的后果比页面结构id轻,但不是零——渐变、裁剪路径、遮罩这类东西是靠id互相引用的,两个图标各带一份同名的裁剪路径,后加载的那个用的就是前一个的形状。这类视觉bug通常表现为某个图标莫名其妙缺一块,然后被归因为浏览器兼容问题。一页图片的属性怎么批量体检那篇的检查项里没有这一条,因为内联SVG根本不走img标签那条路。

空名字那一档更简单:写成id等于空字符串的元素有1291个,出现在100个页面、71个站上。它谁也指认不了,也没人能指认它,纯粹是模板里一句取值没取到留下的痕迹。

六成页面撞了名,撞的是些什么?

页面结构id这一档,181个页面上一共1508组。

指标数值
带id的元素总数41267个,每页中位169个
至少有一处撞名的页面117 / 181=64.6%
至少有一页撞名的站79 / 127=62.2%
卷进撞名的元素4672 / 41267=11.3%
一个名字被用几次中位2次,四分之三的组也是2次,最多314次
一页都不撞的站48个

中位数是2这件事挺重要。绝大多数撞名不是什么灾难现场,就是同一个名字被用了两次。麻烦的地方恰恰在这儿:两次太不起眼了,不会有人在控制台里看见,也不会有任何工具主动告诉你。

中位数是2,最大值是314

另一头则完全是另一种画风。parachutehome.com的首页上,同一个名字用了314次——是分期付款组件Affirm挂在每一张商品卡上的容器,商品卡有多少张就有多少份。同一个站上还有另外两个同类的名字各用了104次和32次。casetify.com的首页有一个叫layer-6-checkbox的名字用了56次,另一个叫menu-list-checkbox的用了37次,这是纯CSS折叠菜单的经典写法:一个看不见的复选框加一个标签,点标签就等于勾复选框。名字全一样的话,五十六个标签点下去勾的是同一个复选框。

按站排一下,前几名是:everlane.com两个页面加起来228组、thirdlove.com 143组、misen.com 140组、menuspace.com和parachutehome.com各125组,casetify.com一个首页就69组。

撞上的那份,为什么七成八当场看不见?

这是这一轮最出乎意料的数字。1508组里,文档顺序排第一的那个元素在页面加载完的那一刻当场看不见的,有1180组,占78.2%。

第一个为什么看不见组数占1180的比例
display:none55747.2%
visibility:hidden41535.2%
宽高为零14912.6%
祖先带aria-hidden332.8%
透明度为零201.7%
在未展开的折叠面板里60.5%

为什么偏偏是第一个

把撞名元素落在哪片区域数一遍就明白了:带移动版特征的992组、导航里的664组、弹层里的360组、带桌面版特征的338组、页脚323组、页头305组。

这就是电商模板最常见的那套做法:同一份导航渲染两遍,一份给手机一份给桌面,用CSS各自藏一半。而移动版那一份在HTML里通常写在前面。于是桌面用户看到的是第二份,所有按名字找元素的动作却都落在第一份上——那份他永远看不见。

innisfree.com的首页上有两个叫customer_login_link的链接,一个在页头一个在移动菜单里;ugreen.com的巨型菜单里,submenu-1这个名字用了10次;flyingtiger.com的Details-menu-drawer-submenu-1用了13次,两个页面上都一样。这些都是同一个模式。

顺带说一句,这套双份渲染的做法本身还有别的账要算。类目导航改版做了三轮那篇讲过同一套导航被两拨人各维护一份会怎么走形,撞名只是它在源码层的一个副作用。

第一个看不见的时候,页面会怎么样?

光有比例还不够,得知道后果长什么样。这一节的数据不是从这批站上量的,是拿最小复现页面在真浏览器里跑出来的:一个链接指向某个名字,页面上有两个同名的目标,第一个用不同方式藏起来,看点下去会发生什么。

第一个的状态点完滚到哪用户看到什么
display:none纹丝不动地址栏多了个井号,页面停在原地
visibility:hidden滚到第一个的位置滚过去了,那块是空白
透明度为零滚到第一个的位置同上
宽高为零滚到第一个的位置同上
在未展开的折叠面板里滚到第一个的位置浏览器主动把折叠面板展开了
hidden=until-found滚到第一个的位置浏览器主动把hidden撤了

三种后果分得很干净。display:none这一档最糟——浏览器量不出这个元素在哪儿,干脆不动,用户点了一个链接,地址栏变了,页面一动不动,绝大多数人会以为是自己没点中,再点一次,还是不动。这一档在实测数据里占47.2%。

第二档是滚过去了但那儿什么都没有,占了另外将近一半。它比第一档更难查,因为页面确实响应了,只是停在一片空白上,看上去像样式没写好。

最后两行是好消息:折叠面板和hidden=until-found这两种藏法,浏览器会主动替你揭开。这是这些年才加进去的行为,为的就是让搜索结果里的深链能落到折叠内容上。至于折叠内容本身在机器眼里算不算数,16种藏法的实测裁决表那篇按藏法逐个判过,结论跟这里是一致的:能被揭开的那几种,机器也认。

标签那一头的实验结果更干脆。两个同名的输入框,第一个display:none,标签的for指过去——点标签,焦点哪儿都没去。不是落到隐藏那个,也不是落到可见那个,是根本没有元素拿到焦点。用户点了那行说明文字,光标不出现,输入框不聚焦,什么反馈都没有。

这类问题不会产生任何一条报错,不会出现在任何一张监控图上。它只表现为某一小撮用户在这一步多点了几下,然后放弃。下拉框那篇拆过同一种沉默:控件选型错了不会报警,只会在转化数字上留下一点谁也归不了因的损耗。

同一个组件渲染两遍,和两个不相干的东西撞名,哪种更难查?

把每组撞名元素往上数四层祖先,链完全一样的算同一个组件的多次渲染,不一样的算两个不相干的东西撞了名。结果几乎对半:同组件多次渲染804组占53.3%,不相干撞名704组占46.7%。

这两种的性质完全不同。

不相干撞名才是会咬人的那一类

同组件多次渲染是模板问题,改起来清楚:给循环里的每一项拼上一个唯一后缀就行,商品ID、变体ID、索引号都可以。麻烦在于它数量大,一改就是一片。

不相干撞名才是真会咬人的那一类。bugaboo.com的首页上有三个叫csrf_token的隐藏输入框,祖先链完全不同——是三张不同的表单各自带了一个同名的安全令牌。表单提交时脚本如果按名字去取这个值,取到的永远是第一张表单里那个。这类问题一旦出错,表现是提交被服务端拒掉,而所有人第一反应都是去查后端。至于一张表单到底把数据交给谁、隐藏域里装了些什么,收邮箱那张表一半没写去向那篇整篇都在拆这件事。

再举一个更隐蔽的。reebok.com的首页上有两个叫header-account的链接,一个在桌面版按钮区,一个在移动版抽屉里。它们指向同一个功能,写这段代码的两拨人多半也不知道对方存在。

商品页为什么比首页糟一倍?

把首页和商品页分开算,差距大得不像同一批站。

分档页数有撞名的比例中位撞名组数中位id元素数
首页12755.9%1组111个
商品页5485.2%5组249个
Shopify系9582.1%3组256个
非Shopify8645.3%0组70个

商品页比首页高出将近三十个百分点,原因不复杂:商品页上有一整套按变体循环出来的东西——尺码、颜色、数量、加购、库存提示,每一项都要一个能被标签指认的名字,而这些名字往往是从模板变量拼的。首页上循环出来的主要是商品卡,卡上要指认的东西少得多。

Shopify系高出非Shopify将近一倍,但这句话得小心解读。Shopify主题的id数中位是256个,非Shopify只有70个。不是Shopify的模板写得差,是它往页面里放的可指认元素本来就多三倍多,分母大了撞车概率自然高。这跟DOM元素过多实测里那条1400的线是同一件事的两面。同一套模板把同一段内容铺很多遍这件事还有更极端的形态,一张礼品卡的结构化数据119KB那篇里同一段配送政策抄了60遍。

还有个对照可以当作站内一致性的检查:54个站首页和商品页都抓到了,两页结论一致的43个,79.6%。五分之一的站,只看首页会得出跟只看商品页相反的答案。所以这类自查不能只查首页。

加入购物车那个按钮,同名的有十四个

抽象比例讲完了,来看几个具体的。下面这些都是从实测数据里直接翻出来的,每一条都能自己去页面上验。

italic.com的商品页上,叫pdp-add-to-cart-button的按钮有14个,第一个是visibility:hidden,藏在一个弹层里。也就是说,任何按这个名字去找加购按钮的脚本、埋点、自动化测试,找到的都是那个弹层里的隐形按钮,不是用户真正会点的那个。同一个站的首页上也有13个。

尺码、订阅、收藏,撞的都是转化链上的那几格

gymshark.com的商品页更典型。七个尺码选项各自有两个同名的输入框,名字是product加变体ID加size拼出来的,第一个宽高为零。这一页上还有tab-1、tab-2、tab-3三个按钮各重了两次。尺码选择器是商品页上转化链条最靠前的一环,而它的名字在这一页上没有一个是唯一的。尺码表实测55个站那篇数的是这张表在不在,这里数的是选尺码那几个框叫什么。

dollarshaveclub.com的商品页上,subscribe-radio-button、ship-now-subscriber、add-one-time-to-next-box-subscriber这三个订阅相关的单选框各有两份,第一份都是display:none,都在移动版那一侧。用户在桌面上点的是第二份,任何按名字读取当前选中项的脚本读的是第一份。

innisfree.com的首页上,商品快速选购弹层里的ProductSelect下拉框和SingleOptionSelector输入框各有两份,连它们外面那两张表单也是同名的。这一组的祖先链完全一致,说明是整个快速选购组件被渲染了两遍。

搜索框也是重灾区。search-input是这批数据里跨站撞得最多的一个名字,burrow.com、underarmour.com、lookfantastic.com三个站的首页上各有两个。前两个站的第一份都是visibility:hidden,第三个站的第一份倒是可见的——同一个名字撞两次,后果还不一样。独立站搜索框怎么设计那篇讲过这个入口的转化价值有多高,而它在源码里同时存在两份、脚本只认得其中一份。

theordinary.com的首页上有11个叫wishlistUrl-grid的隐藏输入框,各自装着一个收藏接口的地址。第一个是display:none。点收藏的时候如果脚本按名字取地址,十一件商品收藏的会是同一件。

保哥前阵子给一个户外装备独立站排查过一个类似的现象:客服反馈说有用户投诉尺码选不动,但复现不出来。最后发现是主题升级之后,桌面版和移动版的尺码选择器共用了同一套名字,而某个第三方尺码推荐插件按名字去读当前选中值,读的永远是隐藏那份。桌面用户点了尺码,插件那边一直显示未选择。这个bug的表现是间歇性的,因为只有装了那个插件的页面才犯。

这些名字是谁起的?

把181个页面上全部34433个不重复的名字按写法分了个类,能看出这些id大部分不是人一个一个想出来的。

写法名字数占比典型样子
语义化的短横线写法1266136.8%card-image-link
归不了类的629618.3%混合大小写、下划线、中文
短数字结尾617717.9%tab-1、submenu-2
长数字结尾367110.7%product-card-image-7243977588887
平台自动生成29248.5%shopify-section-template--…
以数字开头13864.0%18008641、2026WebRevamp_HomeBanner_10132
名字里带空格5081.5%Mini Category Icon、pause button
组件库生成2740.8%headlessui-、radix-
框架运行时生成1870.5%:r7:这种
哈希串1830.5%一长串十六进制
空字符串1110.3%id=""
UUID550.2%标准三十六位

短数字结尾那一类最容易出事

短数字结尾那17.9%值得单说。tab-1、submenu-2这类名字在单个组件里没问题,一旦这个组件在一页上出现两次就必撞。它们贡献了撞名组里的267组。

带空格和以数字开头这两类,直觉上应该是废名字,实测下来却没有。拿最小页面跑了一遍:id里带空格、以数字开头,getElementById都取得到,井号锚点也跳得过去,唯一会当场报错的是不加转义的CSS选择器——写成井号加9开头的选择器,浏览器直接抛异常,得先转义。所以这两类是规范违反、功能不坏,麻烦留给了写脚本的人。翻一眼真实的名字就知道它们是怎么来的:snowpeak.com的Mini Category Icon、brooklinen.com的pause button,全是设计稿里的图层名被原样带了出来;lookfantastic.com的18008641和getquip.com的24301532则是后台的商品编号直接当了名字。

这些名字有一半是机器拼出来的

还有几个名字的来历一眼能看出来。oclean.com的页面上有两个id叫span-style-color-f-61-a-1-a-class-stk-hi,还有一个叫validated-by-a-2-min-brushing-period-twi。这是WordPress的一个区块插件按标题文字自动生成id,而那段标题文字里本身混着HTML标记,于是标记也被一起转成了名字的一部分。同一个页面上还有六个叫图层_1的——中文的图层,同样是从设计稿导出来的。

thirdlove.com的商品页上有11个style标签,id全叫isPasted。这是富文本编辑器的标记:有人在后台粘贴内容时,把编辑器自己的痕迹一起粘了进来,粘一次留一个,留了十一个。HTML注释里那些内部痕迹那篇说过页面会不小心记下很多本不该外露的东西,这是同一类东西的另一种形态。

tentree.com的首页则是另一个方向:它把商品名直接转成了id,womens-dunes-shacket-forest-river-green这种,同一件商品出现在几个轮播位里就重几次。这类自动生成的名字跟3万条!important那篇里数出来的类名是同一批产物:都是构建工具或者可视化编辑器批量吐的,没有人逐个看过。

只curl一次能看见多少?读三遍呢?换成手机呢?

这一节回答的是这套读数本身准不准。

先看原始HTML和渲染后的对照。每个页面在浏览器里再发一次请求拿到服务端吐出的那份HTML,用正则把id数一遍,和渲染完的DOM比:

181个页面里,两边撞名组数完全一致的只有28.2%;渲染后变多的51.4%,变少的20.4%。原始HTML里就已经有撞名的页面占76.8%,撞名id总数从只curl的2467涨到渲染后的2794。四分之三以上的撞名是服务端模板直接吐出来的,剩下的是脚本运行之后才添上的。

变少那20.4%更有意思。tentree.com的首页原始HTML里有25组撞名,渲染完只剩1组——脚本把重复的商品卡清理掉了。所以只看源码会高估,只看DOM会低估,两边都得看。这跟JS渲染实测131个站那篇的结论方向一致:渲染前后的差异不是单向的。至于哪些第三方脚本会在渲染阶段往页面里塞东西,只看HTML一个域名都看不到那篇把这条通道摊开过。

再看重复读数的稳定性。挑了30个站——撞名最多的、只有一处的、一处都没有的各取一批——每个站的首页用桌面视口连读三遍,再换手机视口读一遍。

29个站拿到了完整的四遍读数。桌面视口连读三遍,撞名组数完全相同的是29个站,一个都没变;换成手机视口之后与桌面一致的27个,93.1%;因为换视口而让结论翻转的,零个。页内锚点数同样是三遍全相同。

三遍完全一样这件事值得强调。它说明这些撞名不是抓取时的偶然,不是脚本随机渲染的产物,也不是这套尺子自己有随机性——它们固化在模板里,天天如此

换视口那一遍更有信息量,因为它直接检验了前面那个解释。如果撞名主要来自移动版和桌面版各渲染一份,那么换个视口读数就该大幅变化。结果是93.1%的站纹丝不动。两份都在HTML里,跟视口无关,CSS只负责各藏一半。

只有两个例外,都值得看一眼。bellroy.com在桌面视口下有230个带id的元素、20组撞名,换成手机视口只剩141个元素、2组撞名——它是真的按视口渲染不同的DOM。brooklinen.com则是15组变16组,差一组,属于噪音。

顺带一个反向的对照:撞名组数三遍完全稳定,但id总数并不都稳定。stokke.com三遍分别读到536、620、620个带id的元素,手机视口下是716个;burrow.com是37、53、54。会动的在动,不该动的没动,这个对照本身就说明脚本的解析没有歪。

照着改,先动哪几处?

这套自查的成本很低,第一步只要一行代码。

第一步,在任意一个页面的控制台里跑这一行,拿到本页所有重名的名字:

const ids = [...document.querySelectorAll('[id]')].map(e => e.getAttribute('id'));
console.log(ids.filter((v, i) => ids.indexOf(v) !== i));

注意这里用的是getAttribute而不是点id。表单里那个叫id的字段会把表单自己的名字顶掉,直接读属性能绕开这一类坑。

第二步,别只在首页跑。前面那个79.6%说明五分之一的站首页和商品页结论相反,商品页才是重灾区,商品页里又以带变体选择的那类最狠。

第三步,拿到重名清单之后先分档,不要一股脑全改。判据是这个名字有没有人在指认它:

  • 被label的for指着的——最高优先,它直接对应用户点了没反应;
  • 被井号锚点指着的——次高,对应链接点了页面不动;
  • 被aria-labelledby、aria-controls这类属性指着的——影响读屏软件和读无障碍树的智能体
  • 只是挂在那儿没人指认的——可以排到最后,但别忘了脚本随时可能开始用它。

先改被指认的,再改没人指认的

第四步,处理最常见的那种模式,也就是移动版桌面版各渲染一份。三条路:一是给两份加不同前缀,二是改成一份DOM用CSS调整布局,三是把不需要重复的那部分抽出来共用。第一条最省事也最不容易出错。

第五步,循环里的名字必须拼唯一后缀。商品ID、变体ID都行,索引号也行但得连组件实例一起拼,否则第二个轮播的第一项还是会和第一个轮播的第一项撞上。tentree.com那个例子就栽在这儿。

第六步,别忘了标记本身的老账。过时HTML标签实测那篇说过换个平台只是换一批老代码,重名这件事同理——迁移完记得重跑一遍,别指望新模板天生干净。语义化标签那篇里那套判断也能顺手用上:一个元素该用什么标签、要不要名字,本来就是同一个问题的两面。

第七步,把这一行自查挂进上线前的检查里。每次换主题、装插件、加营销代码片段之后跑一次,成本几秒钟。第三方脚本两天半里就有四分之一的站自己变了,指望装完一次就长期不变是不现实的。

最后提醒一件容易被误会的事。WCAG 2.2把要求id唯一的那条准则废掉了,不等于这件事不用管了。废掉的理由是它作为合规判定项产出的噪音太多,跟后果存不存在无关。上面那些点了没反应的标签、跳不动的链接、读错值的脚本,一条都没因为准则废除而消失。

常见问题解答

一个页面上有重复的id,会影响SEO排名吗?

直接影响没有证据支持,Google没有把标记合法性列为排名因素。但间接影响是实打实的:页内锚点跳不动会让搜索结果里的段落级深链落空,标签关联断掉会让无障碍树上的元素拿不到正确名字,而读无障碍树的不只是读屏软件,还有越来越多的AI代理。把它当成可用性问题处理,比当成排名问题处理更准确。

怎么快速判断本页有没有重复id?

控制台里跑一行:把所有带id元素的id取出来成数组,再筛出索引位置和首次出现位置不一致的那些,剩下的就是重名清单。取id时用getAttribute,别用点id属性,否则遇到表单里的同名字段会拿到元素而不是字符串。

重复的id会让页面报错吗?

不会。浏览器不报错、不警告,页面照常渲染。CSS选择器对所有同名元素都生效,所以视觉上通常看不出任何异常。只有在有人点标签、点锚点或者脚本按名字取元素的时候才会暴露,而且暴露形式是无声的——动作没有发生,没有任何提示。

第一个同名元素是隐藏的,锚点跳转会怎样?

取决于怎么隐藏的。用display:none藏的,浏览器量不出位置,页面完全不动,只有地址栏多了个井号。用visibility:hidden、透明度为零或者宽高为零藏的,页面会滚到那个位置,用户看到一片空白。藏在未展开的折叠面板里或者用hidden=until-found标记的,浏览器会主动展开再滚过去,这两种反而是安全的。

id里可以有空格或者以数字开头吗?

规范说不行,id不能含空白字符。但实测下来这两种写法getElementById取得到、井号锚点也跳得到,唯一会当场出问题的是不转义的CSS选择器——以数字开头的id直接写进选择器会抛异常,必须先做转义处理。所以它们属于规范违反但功能不坏那一类,真正的代价是让后来写脚本的人踩坑。

为什么商品页的重复id比首页多这么多?

因为商品页要按变体循环渲染一整套需要被指认的控件:尺码、颜色、数量、加购按钮、库存提示,每一个都得有名字给标签去关联。这些名字多半是从模板变量拼出来的,拼的时候少带一个变体ID就会重。实测数据里商品页有重名的比例是85.2%,首页是55.9%。

内联SVG图标里的重复id要不要管?

比页面结构的id缓一档,但不是完全不用管。渐变、裁剪路径、遮罩这些是靠id互相引用的,两个图标各带一份同名的裁剪路径,后面那个会用上前面那个的形状,表现为图标缺一块。实测里这类撞名有397组、分布在50个站上,最常撞的名字是Vector和Layer_1,都是设计工具导出时的默认图层名。

权威参考资料

分享到
标签
版权声明

本文标题:《id重复的页面占六成,浏览器只认第一个,而它多半看不见》

本文链接:https://zhangwenbao.com/duplicate-element-id-audit.html

版权声明:本文原创,转载与引用请注明作者与原文链接。许可协议: CC BY 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交