理解 HTML、XML 和 XHTML
或者,闭合你的 <script>
和 <canvas>
标签!
HTML、XML 和 XHTML 之间的关系在网络上是一个相当混乱的领域。我们经常在 webkit-dev
邮件列表中看到有人疑惑为什么他们看似 XHTML 的文档却产生了 HTML 输出。或者我们会被问到为什么像 <b />
这样的 XML 构造实际上并不能闭合 bold 标签。
本文将尝试澄清其中的一些困惑。
你可能想知道副标题与主标题有什么关系。嗯,HTML/XHTML 的区别可能看似是一个晦涩的话题,但它可能产生重大的实际影响。特别是,它很可能在即将到来的 WebKit 版本中极大地影响 Dashboard Widget 开发人员。我将在最后进一步解释。
什么是 HTML、XML 和 XHTML?
万维网的原始语言是 HTML (HyperText Markup Language),通常以其当前版本 HTML 4.01 或简称为 HTML4。HTML 最初是 SGML (Standard Generalized Markup Language) 的一种应用,SGML 是一种用于创建标记语言的元语言。SGML 相当复杂,实际上大多数浏览器并不会完全遵循它的所有怪异之处。实际上在网络上使用的 HTML 最好的描述是受 SGML 影响的一种自定义语言。
关于 HTML 的另一个重要点是,所有 HTML 用户代理(这是一个概括性术语,指代读取 HTML 的程序,包括网络浏览器、搜索引擎爬虫等等)都具有极其宽松的错误处理。许多技术上不合法的结构,如错误嵌套的标签或无效的属性名,都被允许或安全地忽略。这种错误处理在浏览器之间相对一致。但在边缘情况下存在许多差异,因为这种错误处理行为没有被文档化或成为任何标准的一部分。这就是为什么验证你的文档是一个好主意。
XML 和 XHTML 则完全不同。XML (eXtensible Markup Language) 源于希望能够在网络上使用除 HTML 固定词汇表之外的更多内容。它是一种元标记语言,类似于 SGML,但它简化了许多方面,使其更容易创建通用解析器。XHTML (eXtensible HyperText Markup Language) 是 HTML 在 XML 语法下的重新表述。虽然在许多方面非常相似,但它有一些关键区别。
首先,XML 总是需要闭合标签,并且对于不需要闭合标签的标签有特殊的语法。在 HTML 中,一些标签,如 img
,总是被认为是空的并且会自动闭合。另一些标签,如 p
,可能会根据其他内容隐式闭合。还有一些标签,如 div
,总是需要有闭合标签。在 XML(包括 XHTML)中,任何标签都可以通过在代码尖括号之前放置一个斜杠使其自闭合,例如 <img src="funfun.jpg"/>
。在 HTML 中则仅仅是 <img src="funfun.jpg">
。
其次,XML 有严厉的错误处理规则。与 HTML 解析器的宽松不同,XML 解析器要求在遇到 XML 文档中最简单的语法错误时也要灾难性地失败。这让你更有可能生成有效的 XML,但同时也使得一个微不足道的错误很容易彻底破坏你的文档。
HTML 兼容的 XHTML
当 XML 和 XHTML 首次标准化时,没有浏览器原生支持它们。为了至少能部分使用 XHTML,W3C 提出了一种称为“HTML 兼容的 XHTML”的方法。这是一组指导方针,用于创建有效的 XHTML 文档,这些文档仍然可以或多或少地被处理为 HTML。基本思想是对于 HTML 不需要闭合标签的地方,如 img
,
br
或 link
,使用自闭合语法,并在斜杠前加一个额外的空格。因此,我们常见的图片示例会是这样:<img src="funfun.jpg" />
。详细信息在 XHTML 1.0 标准的 附录 C 中描述。
需要注意的是,这算是一种 hack,并且依赖于 HTML 解析器实际上的错误处理行为。它们并不真正理解 XML 的自闭合语法,但这样写会使它们将 /
视为一个属性,然后丢弃它,因为它不是一个合法的属性名。而且如果你尝试使用像 <div />
这样的写法,它们不会理解这个 div
应该是空的。
HTML 和 XHTML 之间还有许多其他细微差别,这些简单的语法 hack 无法涵盖。在 XHTML 中,标签名是大小写敏感的,脚本的行为方式略有不同,并且像 <tbody>
这样缺失的隐式元素不会由解析器自动生成。
因此,如果你以这种风格编写一个 XHTML 文档并将其作为 HTML 处理,你实际上根本没有获得 XHTML——之后尝试将其视为 XHTML 可能会导致各种问题。
如何判断我的文档是 HTML 还是 XHTML?
你可能会对前面几节关于将 XHTML 作为 HTML 处理的讨论感到有些困惑。毕竟,如果我的文档是 XHTML,那故事应该就结束了,对吧?毕竟,我放了一个 XHTML doctype!但事实证明事情并非如此简单。
那么到底是什么决定了文档是 HTML 还是 XHTML 呢?唯一决定文档是 HTML 还是 XHTML 的东西是 MIME 类型。如果文档以 text/html
MIME 类型提供,它就被视为 HTML。如果以 application/xhtml+xml
或 text/xml
提供,它就被视为 XHTML。特别需要指出的是,以下任何事情都不会导致你的文档被视为 XHTML:
- 使用 XHTML doctype 声明
- 在顶部放置 XML 声明
- 使用 XHTML 特有的语法,如自闭合标签
- 将其验证为 XHTML
事实上,互联网上绝大多数所谓的 XHTML 文档都是以 text/html
提供服务的。这意味着它们根本不是 XHTML,而是实际无效的 HTML,只是凭借 HTML 解析器的错误处理才得以运行。网络上所有那些“Valid XHTML 1.0!”链接实际上是在说“Invalid HTML 4.01!”。
HTML 可能才是你想要的
也许你现在才意识到,你精心制作的有效 XHTML 文档实际上是无效的 HTML。你有几种选择:
- 以
application/xhtml+xml
提供你的内容。 然而,这可能不是一个好主意。Microsoft Internet Explorer 根本无法处理 XHTML,以这种 MIME 类型提供服务会导致它下载文件。除非你愿意完全屏蔽 IE 用户,否则你可能不应该选择这个选项。 - 对 IE 浏览器提供
text/html
服务,但对其他浏览器提供application/xhtml+xml
服务。 这样你的内容至少有机会在 IE 中工作,并使用 HTML 兼容的 XML 来实现其最初的目的,作为一种回退兼容性 hack。然而,这仍然有缺点。你的文档在 IE 与其他浏览器中的处理方式将完全不同。由于严格的错误处理规则,一个完全有效的 HTML 构造可能会完全破坏 XML 解析。或者反过来,一些有效的 XHTML 更改可能会导致 HTML 文档看起来不正确。此外,支持 XHTML 的浏览器其 XHTML 模式远不如 HTML 模式成熟或经过良好测试。Safari 就是这种情况。而且 Mozilla 也 不鼓励这种做法,原因是缺乏对增量渲染的支持。他们还在 处理 XHTML 与 HTML 的许多区别 中列出了一些差异。 - 维持现状。 另一种选择是坚持现状——生成 XHTML 内容但将其作为 HTML 提供服务。这里的缺点主要是你无法使用 HTML 验证器(它们会以与浏览器解析方式匹配的方式验证文档);而且如果你的文档最终确实被处理为 XHTML,你冒着出现细微不兼容的风险。但这这也引出了一个问题:你认为使用 XHTML 会得到什么好处?你可能听过很多关于它的宣传,专家可能告诉你它是下一个大趋势,但如果最终它只是被视为 HTML 标签汤,你又能获得什么好处呢?
- 提供有效的 HTML。 这是我推荐的选项——以
text/html
MIME 类型提供有效的 HTML 文档。这样你将使用经过良好测试的网页浏览器模式,不必过多担心奇怪的兼容性问题,并且能从基于 HTML 的工具链中获得最大益处。
因此,总的来说,似乎最好是选择 HTML 并始终如一地遵循。但不要只听信我的话。像 Ian Hickson、Anne van Kesteren 和 Mark Pilgrim 等领先的网页标准专家都指出了将 XHTML 作为 HTML 提供服务的陷阱。
最佳实践
在当今的网络上,最好的做法是始终使用 HTML4 来创建文档。完全的 XHTML 处理不是一个可行的选项,因此最好的选择是始终坚持使用 HTML4。以下是实现这一目标的最佳方式:
- 使用 HTML4 doctype 声明,最好是那种能在网页浏览器中触发“标准模式”的声明。一个 HTML4 标准模式 doctype 的例子是
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
。 - 使用
text/html
MIME 类型提供你的内容,或者对于本地内容,赋予它.html
或.htm
后缀。这将引导浏览器、搜索引擎和其他应用程序将你的内容正确处理为 HTML。 - 将你的内容验证为 HTML,而不是 XHTML。一个便捷的方法是使用验证服务,例如 W3C Validator。 (但要注意,验证器查看的是你的 doctype 而不是 MIME 类型,这与浏览器不同。) 非
不幸的是,有时你并不能完全控制你生成的内容。例如,这篇博客就是使用 WordPress 标签发布的。如果你也处于同样的情况,鼓励你的工具供应商提供支持生成有效 HTML 的功能。
关于那些 <script> 和 <canvas> 标签
我在这篇文章开头承诺会告诉大家这与 Dashboard widgets 中闭合 script 和 canvas 标签有什么关系。结论是,HTML 中的 XML 风格的自闭合语法并不总是无害的。
WebKit 的 Safari 2.0 版本有一个特殊的怪异之处,它会将使用自闭合语法(像这样:<script src="myscript.js" />
)的 script
元素视为已正确闭合。当时像 Firefox 这样的基于 Gecko 的浏览器也有类似的怪异之处,我们决定模仿它以兼容特定的网站。然而,未来版本的 Firefox 将移除这一怪异之处,并且基于 HTML 的未来标准,例如 Web Apps 1.0,将明确禁止这种行为。因此,我们也很可能会在未来的 WebKit 版本中移除这一怪异之处。不幸的是,依赖这种解析怪异之处的 HTML 已经渗透到大量的 Dashboard widgets 中。一个不支持这种怪异之处的 WebKit 将导致 widget 损坏——外部脚本代码将永远不会运行。
canvas
元素也有类似的问题,因为它正在经历标准化过程。canvas
最初在 Safari 中作为像 img
这样的空标签实现,但标准和其他浏览器都倾向于要求显式闭合标签,以支持回退内容。Widgets 在这里遇到了两种不同的陷阱——许多使用 XML 自闭合语法(<canvas />
),而另一些则只是一个普通的未闭合(<canvas>
)标签。无论哪种情况,你都需要改为使用显式闭合标签(<canvas></canvas>
),否则未来的 WebKit 版本会认为文档的其余部分都在 canvas
元素内部,而不会渲染它。
结论
关于 HTML 和 XHTML,很容易产生混淆,而且许多专家对此给出误导性的建议。幸运的是,大多数时候这无关紧要。但有时却很重要,并且会严重破坏你的内容。因此,请确保你理解它们之间的区别,并提供一些干净、良好的标记。