为啥浏览器可以推断出某些省略的 HTML 元素,但不能推断出形成有效标记所需的所有省略的元素?

Posted

技术标签:

【中文标题】为啥浏览器可以推断出某些省略的 HTML 元素,但不能推断出形成有效标记所需的所有省略的元素?【英文标题】:Why can browsers infer certain omitted HTML elements, but not all omitted elements required to form valid markup?为什么浏览器可以推断出某些省略的 HTML 元素,但不能推断出形成有效标记所需的所有省略的元素? 【发布时间】:2018-04-01 03:54:24 【问题描述】:

考虑以下无效 html,其中 <tr><div> 的直接子代:

console.log(document.getElementsByTagName('tr').length);
<div>
  <tr></tr>
</div>

&lt;tr&gt; 元素未添加到 DOM。

现在考虑同样无效的 HTML,其中 &lt;tr&gt;&lt;table&gt; 的直接子代:

console.log(document.getElementsByTagName('tr').length);
<div>
  <table>
    <tr></tr>
  </table>
</div>

这一次,&lt;tr&gt; 元素确实被添加到 DOM。

请注意,我故意省略了第二个 sn-p 中的 &lt;tbody&gt;,即 required 以形成有效的标记。如果省略,浏览器会自动添加&lt;tbody&gt;,如this question 中所述。

This answer 提到了官方 W3 文档的 fairly extensive list 关于 what 标签是可选的,但 why这些特定标签是可选的吗?考虑到浏览器足够智能,可以自动添加我省略的&lt;tbody&gt; 元素,为什么还不够智能添加&lt;table&gt; 元素呢?不会有歧义,因为&lt;table&gt;&lt;tbody&gt;only valid parent

为什么可以从&lt;tr&gt; 推断出&lt;tbody&gt;,而不能从&lt;table&gt; 推断出?是否只能推断出一层 DOM 层次结构?

【问题讨论】:

&lt;tr&gt; 作为&lt;table&gt; html.spec.whatwg.org/multipage/tables.html#the-tr-element 的直系子代不是无效的 “这是形成有效标记所必需的” - 不,不是。您在这里链接回 1998 年。可以说,与...当前的千年。 w3.org/TR/html5/tabular-data.html#the-tbody-element: "如果 tbody 元素中的第一个元素是 tr 元素,并且该元素之前没有紧跟结束标签的 tbody、thead 或 tfoot 元素,则可以省略 tbody 元素的开始标签被省略了。” 这与“浏览器智能”无关,而是故意这样指定 您所指的可选标签的广泛列表主要由可选的 end 标签组成。现在,正确确定如何或何时关闭当前打开的元素,比确定元素的 正确 父元素是什么要容易得多 - 特别是对于像 tr 这样的元素,它可能有几种不同的元素类型作为父元素。 【参考方案1】:

在这种特定情况下:

<div>
  <tr></tr>
</div>

...tr 元素没有出现在 DOM 中的原因是 HTML 解析算法要求 HTML 解析器完全忽略它。

该案例的 HTML 规范的相关部分是规范的 Tree construction 部分,特别是在 The "in body" insertion mode 小节中,它说:

标签名称为以下之一的开始标签:“caption”、“col”、“colgroup”、“frame”、“head”、“tbody”、“td”、“tfoot”、“ th", "thead", "tr"Parse error。忽略令牌。

相比之下,对于这种情况:

<div>
  <table>
    <tr></tr>
  </table>
</div>

...规范的相关部分是The "in table" insertion mode 小节,其中说:

标签名称为以下之一的开始标签:“td”、“th”、“tr” 将堆栈清除回表上下文。 (见下文。)

           为没有属性的“tbody”开始标记标记插入 HTML 元素, 然后将插入模式切换为“in table body”。

           重新处理当前令牌。

...The "in table body" insertion mode 小节说:

标签名称为“tr”的开始标签 将堆栈清除回表体上下文。 (见下文。)

          为令牌插入一个 HTML 元素,然后将插入模式切换为“行内”。


因此,一般而言,对于任何关于为什么 HTML 解析器以某种方式处理特定上下文中的任何给定开始标记或结束标记的问题,答案是 HTML 规范中有一些 HTML 解析算法的小节明确定义了如何解析器必须为可能在其中找到的每个特定上下文处理该开始标记或结束标记。

【讨论】:

【参考方案2】:

过去创建的表是由表和行组成的,根本没有 tbodythead 元素。

即使你提到的提到tbody 是“必需的”,实际上也根本没有这么说。下一句说如果table 之后的第一个元素是`tr,则开始标签是可选的。

另见此处:

https://www.w3.org/TR/html5/tabular-data.html#the-tbody-element

这是官方的并且声明相同:

如果 tbody 元素中的第一个元素是 tr 元素,则可以省略 tbody 元素的开始标记

标签的可选性在很大程度上是因为 html 过去通常写得比较草率,没有像 &lt;br /&gt; 这样的空标签的概念,而且通常不需要像 &lt;li&gt; 这样的结束标签.

在 HTML 4 之后尝试创建更严格的 XHTML 标准,并且没有大部分(或任何?)可选性并强制 html 严格遵守 XML。这从未完全起飞,html5 完全相反,将 HTML 不一定是 XML 的事实编码。

【讨论】:

啊,有趣。我认为文档指出&lt;tbody&gt;可能被省略”意味着它可能被编码器省略,浏览器会自动为你添加它如果省略。考虑到&lt;tr&gt;&lt;tbody&gt; / &lt;thead&gt; 之间的关系存在歧义,我很惊讶&lt;tbody&gt; 可能会被省略,但&lt;table&gt; 本身不会(考虑到&lt;tbody&gt;&lt;table&gt; 之间没有歧义)。 @ObsidianAge HTML 表格是在 HTML 3.0 中引入的。当时,TABLE 下允许存在的唯一元素是 CAPTIONTRtbodythead 是后来才介绍的。 @ObsidianAge html 3 在这里:w3.org/MarkUp/html3/tables.html 和 html 2 规范在这里 ietf.org/rfc/rfc1866.txt 你可以看到尚未定义表格元素。

以上是关于为啥浏览器可以推断出某些省略的 HTML 元素,但不能推断出形成有效标记所需的所有省略的元素?的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用html5的拖拽功能,在火狐中总会弹出一个新面页?

通过js可以对某一HTML元素进行修改。但为啥修改后的源代码没有变?

为啥 Rust 不能推断出 Iterator::sum 的结果类型?

为啥 Crystal 不能在初始化程序中推断出这种类型?

为啥方差注释会导致 Scala 无法推断出这种子类型关系?

为啥 TypeScript 在使用 concat 减少数组时会推断出“从不”类型?