为啥杂散的 </p> 结束标记会生成一个空段落?

Posted

技术标签:

【中文标题】为啥杂散的 </p> 结束标记会生成一个空段落?【英文标题】:Why does a stray </p> end tag generate an empty paragraph?为什么杂散的 </p> 结束标记会生成一个空段落? 【发布时间】:2012-07-19 05:33:28 【问题描述】:

显然,如果您有一个 &lt;/p&gt; 结束标签,而 body 元素中没有匹配的开始标签,那么大多数(如果不是所有)浏览器都会在其位置生成一个空段落:

<!DOCTYPE html>
<title></title>
<body>
</p>
</body>

即使结束标记周围存在任何文本,它也不会成为此 p 元素的一部分——它将始终为空,并且文本节点将始终独立存在:

<!DOCTYPE html>
<title></title>
<body>
some text</p>more text
</body>

如果body 的上述内容被包裹在&lt;p&gt;&lt;/p&gt; 标签中......我会让你猜会发生什么:

<!DOCTYPE html>
<title></title>
<body>
<p>some text</p>more text</p>
</body>

有趣的是,如果&lt;/p&gt; 标记前面没有&lt;body&gt;&lt;/body&gt; 标记,则除 IE9 和更早版本之外的所有浏览器不会生成一个空段落(IE ≤ 9另一方面将始终创建一个,而 IE10 及更高版本的行为与所有其他浏览器相同):

<!DOCTYPE html>
<title></title>
</p>
<!DOCTYPE html>
<title></title>
</p><body>
<!DOCTYPE html>
<title></title>
</p></body>

我找不到任何引用规定没有相应开始标签的结束标签应该生成一个空元素,但考虑到它甚至不是有效的 HTML,这应该不会让人感到惊讶。事实上,我只发现浏览器使用 p 元素(在某种程度上也使用 br 元素!),但没有任何解释原因。

不过,在使用传统 HTML 解析器和 HTML5 解析器的浏览器中,它是相当一致的,同时适用于 quirks 模式和标准模式。因此,可以推断这是为了向后兼容早期规范或遗留行为。

其实我确实在a somewhat related question的一个回答上找到了this comment,基本就印证了:

标签之所以有效未封闭的原因是,最初

被定义为“新段落”标记,而不是 p 是一个容器元素。相当于 是一个“新行”标记。您可以在 1992 年的文档中看到这样的定义:http://www.w3.org/History/19921103-hypertext/hypertext/WWW/MarkUp/Tags.html 和 1993 年的这个文档:http://www.w3.org/MarkUp/draft-ietf-iiir-html-01.txt 因为在更改之前有网页,并且浏览器解析器一直尽可能向后兼容现有的 Web 内容,所以它是总是可以以这种方式使用

但这并不能完全解释为什么解析器将显式的&lt;/p&gt; 结束标记(带有斜杠)简单地视为...一个标记,并在 DOM 中生成一个空元素。当语法没有像more recently 那样严格定义时,这是否是某些解析器错误处理约定的一部分?如果是这样,它是否记录在任何地方?

【问题讨论】:

【参考方案1】:

需要的在 HTML5 中记录。查看http://w3c.github.io/html/syntax.html#the-in-body-insertion-mode 并向下搜索An end tag whose tag name is "p" 并显示:

如果打开元素的堆栈没有按钮范围内的元素 与令牌的标签名称相同,那么这是一个解析 错误;就好像看到了一个标签名为“p”的开始标签,然后 重新处理当前令牌。

翻译成英文的意思是如果&lt;/p&gt;标签不能与现有的&lt;p&gt;标签匹配,则创建一个p元素。

为什么会这样,更难确定。通常,这是因为过去某些浏览器导致这种情况发生为错误,并且网页开始依赖该行为,因此其他浏览器也必须实现它。

【讨论】:

我完全忘记了 HTML5 中存在深入的规范解析器文档(它规范的,对吧?没有别的说法)。感谢您的链接。 An end tag whose tag name is "br" 的类似行为描述如下:“解析错误。就像已看到带有标签名称 'br' 的开始标签令牌。忽略结束标签令牌。”换句话说,&lt;/br&gt; 被视为&lt;br&gt; 确实如此。 FireFox(我认为是 3.6 和更早版本)过去只在 quirks 模式下执行此操作,但 HTML5 为所有模式指定了它。【参考方案2】:

HTML4 DTD 声明结束标签对于段落元素是可选的,但开始标签是必需的。

HTML4 的SGML declaration 声明omittag 是“是”,这意味着可以隐含开始标记。

结束标签遵循SGML规则:

结束标记关闭,回到匹配的开始标记,所有未闭合的中间开始标记都省略了结束标记

Anonymous block boxes 是为文本节点等内联元素生成的,因此它们不需要被段落元素包裹。

Mozilla bug 数据库中有一个线程解释了这种行为:

Mozilla parses "half-tags" gullibly, leading to XSS security problems

这是Boris Zbarsky的相关评论:

实际上,据我了解,正确解析 SGML/HTML需要 以这种方式行事。也就是说,下一个标签的'

并由Ian Hickson总结:

看来,这里的基本原理是通过延迟任何关闭标记来修复标记,直到所有其他打开的元素都关闭之后,并且不尝试使 DOM 遵循 HTML DTD。

参考文献

SGML Productions

HTML 2.0 Specification

Arguments against SGML

Tag Soup: How UAs handle

Tag Soup: How Mac IE 5 and Safari handle

Web SGML and HTML 4.0 Explained

Testing SGML SHORTTAG support across browsers

Mozilla Bug 226495

Shorttag and Omittag

Jotting on parsers for SGML-family document languages: SGML, HTML, XML

A brief, opinionated history of XML - bobdc.blog

【讨论】:

虽然 HTML 4 有 OMITTAG YES,但在 DTD 中已经声明 p 元素需要一个开始标签,所以它不能隐含在 p 元素中。此外,匿名块框是 CSS 的一个特性,它是基于 DOM 结构生成的,而不是相反。 由于开始标签是必需的,而结束标签是可选的,所以结束标签的存在迫使解析器创建一个开始标签。 匿名块框是基于DOM结构生成的,但文本节点不允许是body元素的直接子节点。因此,如果 HTML 解析器看到没有命名父块元素的文本节点,则 CSS 解析器添加匿名父块元素,显然在转换结束标记的最后一步也将匿名块框视为影子 DOM 的一部分,并且放弃将它们包装在隐含的段落中。 @PaulSweatte - 如果你的论点是正确的,那么p 元素发生的情况对于li 元素或td 元素也是如此,这同样需要一个开始标签,但允许可选的结束标签。但请参阅 software.hixie.ch/utilities/js/live-dom-viewer/saved/1704 。从这里我们可以看到,三个元素的相同模式会产生一个li 元素和一个td 元素,但会产生两个p 元素。这不是 SGML,它不理智,它只是“浏览器所做的事情”。 @Alohci litd 元素都可以将块元素作为子元素,并且分别仅在 ultable 构造内部有效。无论如何,这是一个为什么涉及未记录行为的问题,因此“这只是“浏览器做什么”可能是最好的答案。

以上是关于为啥杂散的 </p> 结束标记会生成一个空段落?的主要内容,如果未能解决你的问题,请参考以下文章

程序中的杂散“\303”和杂散“\215”——为啥?

Flex - XMLSocket 截断 XML 的最后结束标记 - 为啥?

为啥 TinyMCE 显示 HTML 标签?

CentOS 停止更新维护,天下没有免费且不散的宴席!

CentOS 停止更新维护,天下没有免费且不散的宴席!

JavaScript变量等于jQuery选择器创建开始和结束标记。为什么?