← 首页 / 链接里套链接 待审核
a element · content model · adoption agency

链接里套链接

<a> 的 content model 是 transparent(透明),但附带一条硬约束:不得有 interactive content 后代、不得有 a element 后代、不得有带 tabindex 的后代。关键词是**后代(descendant)**而非子元素——只要外层 <a> 的整棵子树里冒出另一个 <a>,无论中间隔了几层、隔着什么,都违规。下面给内外两个 <a> 之间夹一层不同的元素,看解析器分别怎么处理:

普通元素(span / div)夹在中间:嵌套被拆开。 <a>formatting element,会进入 active formatting elements 列表。解析到内层 <a> 时,算法发现「列表里到最近 marker 为止存在 a」,于是触发 adoption agency 算法,先把外层 <a> 隐式闭合——两个 <a> 在 DOM 里落成平级,源码的嵌套意图并未实现。

<object> 夹在中间:嵌套被保留。 <object>(连同 <applet> / <marquee> / <template> / <td> 等)在起始时会往 active formatting elements 列表里插入一个 marker。于是解析内层 <a> 时,「到最近 marker 为止」这段里看不到外层 <a>(它在 marker 的另一侧),不触发 adoption agency,外层 <a> 不被闭合。DOM 里于是真实出现了 aa——能渲染,但 non-conforming

1 · 自由输入:贴任意 HTML,看它解析成什么

下面的输入直接传入 new DOMParser().parseFromString(html, "text/html"),渲染其 body 子树。标红的节点 = 这个 <a> 的祖先链里还有另一个 <a>(即 aa 的违规)。

能解析 ≠ 合法。 解析器的宽容只保证「任何输入都有确定的 DOM」,它永远不报错;conformance 是另一套规则,由 Nu Html Checker 之类的 validator 按 content model 检查。上面 <object> 那个 case,DOM 里确实有 aa,validator 必报 The element a must not appear as a descendant of the a element;而被拆平的 case,解析器替你做了错误恢复,但源码本身仍是非良构的写法,不应依赖这种「被动纠正」的行为。

正确写法:让两个链接并列,而不是嵌套。 HTML 无法表达「链接里再套一个指向别处的链接」——若语义上确有此需要(如「整句可点,其中某词另跳一处」),应改用其他交互方式(点击时用脚本拦截跳转),或重新设计信息结构。需要在链接子树里保留独立内容时,<template> 的内容进入独立的 fragment document,其内部即便有 <a> 也不算外层 <a> 的后代,因而合规。

相关链接

  • HTML Standard · Adoption agency algorithm WHATWG 对应本页「拆开」一档:解析器遇到错误嵌套的 formatting element(如 a / b / i)时,如何重整标签栈、把外层标签隐式闭合或克隆。active formatting elements 列表与 marker 的定义也在 parsing 章。
  • HTML Standard · The a element WHATWG 对应本页 content model:a 是 transparent,但「no interactive content descendant, no a element descendant, no descendant with tabindex」这条后代级约束的原文出处。
  • Nu Html Checker validator.w3.org W3C 官方 conformance 检查器。把本页 <object> 那个 case 输入其中,会得到「The element a must not appear as a descendant of the a element」—— 与解析器的实际行为形成对照。
  • MDN · <a>: The Anchor element developer.mozilla.org 逐项说明 <a> 的技术约束(Technical summary 里的 Permitted content / Implicit ARIA role),以及「<a> 不可嵌套、不可包含交互内容」的可读版解释。
  • MDN · DOMParser developer.mozilla.org 本页解析所用接口:parseFromString(str, "text/html") 走的就是浏览器标准 HTML 解析算法,与页面真实加载时一致 —— 所以这里看到的 DOM 树就是真机行为。