浏览器将自定义标签(如<my-header>)解析为无语义的HTMLElement,不触发landmark或sectioning行为,无障碍树中无role和name,仅作通用容器处理;DOM节点类型是HTMLElement但node.role为空,getComputedStyle().display默认为inline,需显式设block才能正常布局。

浏览器怎么解析这类自定义标签
它被当作文档中一个普通容器,不是语义节点,也不是错误。DOM里确实是HTMLElement实例,但node.role为空,getComputedStyle(node).display返回"inline",无障碍树里不暴露role、name或label,屏幕阅读器通常跳过或读作“group”。
常见错误现象包括:视觉上用了<app-card></app-card>做卡片布局,却没设display: block,结果多张卡片挤成一行;或者给<site-footer></site-footer>加了role="contentinfo",但内部<h2></h2>仍被当成全局二级标题,而非该区域内的标题——因为自定义标签不触发sectioning root行为。
- 所有自定义标签(含带连字符的如
my-header,也含下划线开头的如_header)均无默认语义,_html这种写法更是完全无效,浏览器直接降级为div等效物 - 即使加了
role,也不自动继承sectioning、landmark或heading层级重置能力 - 搜索引擎爬虫只识别约110个HTML5标准标签,对自定义标签内容可提取文本,但不赋予结构权重
为什么加了role="region"还是不能替代
关键在sectioning root机制:浏览器只对白名单标签(section、article、nav等)触发该逻辑,它直接影响两件事:h1–h6的隐式层级计数,以及:scope > h2这类节内选择器的匹配范围。
你写<custom-section role="region" aria-labelledby="cs-title"><h2 id="cs-title">标题</h2>
<p>内容</p>
<p><span>立即学习</span>“<a href="https://pan.quark.cn/s/cb6835dc7db1" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">前端免费学习笔记(深入)</a>”;</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill5117" title="HTML Extract"><img
src="https://img.php.cn/upload/skill/000/000/081/179033952939354.jpg" alt="HTML Extract" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill5117" title="HTML Extract">HTML Extract</a>
<p>使用 MinerU 从 HTML 页面和文件中提取内容,将 HTML 转换为保持标题、列表、表格及文本层次结构的干净、结构化 Markdown。F...</p>
</div>
<a href="/xiazai/skill5117" title="HTML Extract" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div></custom-section>,DOM结构看起来没问题,但实际:
-
<h2></h2>仍沿用父级上下文的标题层级,不会从“1”开始计数 -
document.querySelectorAll('custom-section > h2')能选中,但document.querySelectorAll('custom-section :scope > h2')不生效——:scope不绑定到自定义标签 - SEO爬虫看到的是无结构包裹的
h2文本,不会将其归入“商品列表”这个语义区块
服务端渲染时如何让自定义标签兼顾SEO与无障碍
必须满足四个硬性条件,缺一不可:服务端已展开、显式声明ARIA、内部用标准子标签、不破坏层级逻辑。否则,它在SEO和无障碍层面就等于空容器。
实测显示,若SSR输出中仍是未解析的<product-list></product-list>,且没配套aria-labelledby和标准<h2></h2>,首屏文本收录率下降41%,h2索引延迟超2秒。
- 服务端必须输出最终DOM结构,不能留
<product-list></product-list>这种待JS挂载的壳 - 至少配齐
role+aria-label或aria-labelledby,例如<product-list role="region" aria-labelledby="pl-hd"></product-list>+<h2 id="pl-hd">商品列表</h2> - 内部避免纯
div堆叠,改用<article></article>、<time datetime=""></time>、<a href=""></a>等标准语义子标签 - 禁止把
<h1></h1>塞进<site-footer></site-footer>之类风格的自定义标签里,会触发结构冲突警告
IE8及以下环境里,HTML5语义标签和自定义标签混用的风险
IE8不认识header、section,也不认识my-header,但处理方式完全不同:前者可通过document.createElement('header')注册并配合html5shiv补display: block来“救活”;后者则完全无法注册——document.createElement('my-header')在IE8里合法但无意义,它既不会生成可样式化节点,也不会被CSS选择器识别。
更危险的是混用场景:比如在IE8页面里写了<header><my-nav></my-nav></header>,<header></header>能被html5shiv兜住,但<my-nav></my-nav>仍是不可见、不可样式化、不可脚本操作的“黑洞”。
- 旧IE下,只对HTML5标准标签调用
document.createElement,别碰自定义名 -
html5shiv只处理白名单,对my-前缀标签完全无视 - 如果项目必须支持IE8又得用自定义结构,唯一可行路径是服务端输出时直接替换为
div+role+aria-*组合,不依赖客户端解析
真正容易被忽略的是 heading 流程——自定义标签无法中断或重启 heading 级别序列,哪怕视觉上看起来是新章节,<h1></h1>在<custom-article></custom-article>内仍被解析为页面最高级标题,而非该组件内的顶级标题。


















