首页 HTML5 Dive Into HTML5:它的含义是什么?(续三)

Dive Into HTML5:它的含义是什么?(续三)

0 1.6K

关于浏览器如何处理未知元素的长篇大论

前面我们提到 HTML 5 新增加的几个元素。在我们详细讨论这些元素的使用方法之前,我们应该了解一下这个问题:如果浏览器不能识别这种元素,那么浏览器将如何处理?

每个浏览器都有一个它能支持的 HTML 元素的列表。例如,Mozilla Firefox 的列表存储在 nsElementTable.cpp 这个文件中。没有被列出的元素就是所谓的“未知元素”。未知元素有两个基本问题需要解决:

  1. 这个元素应该是怎样的样式?默认情况下,<p>在顶格和底部都有一个间距,<blockquote>则在左侧有一个缩进,<h1>显示一个更大的字体。但是,未知元素的默认样式应该是怎样的呢?
  2. 未知元素的 DOM 应该是怎样的?Mozilla 的 nsElementTable.cpp 不仅包括它能支持的元素列表,而且包含这些元素可以包含哪些元素。如果你使用两个标记,例如<p><p>,第二个<p>会隐式地将第一个关闭,此时,这两个<p>看上去就像是两个并列的元素,而不是具有父子关系。但是,如果你写的是<p><span><span>不会关闭<p>,因为 Firefox 认为<p>是一个块级元素,能够包含<span>。所以,<span>在 DOM 中表现为<p>的子元素。

不同的浏览器对此有不同的理解(很讨厌,对吧!)。主流浏览器中,Microsoft Internet Explorer 对此的回答是最有问题的,而其他浏览器则多多少少是相似的。

第一个问题回答起来相对简单:不要给未知元素任何特殊的样式。仅仅让它们显示成在页面上继承的 CSS 样式就可以了,让页面作者自己去处理这些 CSS。大部分时间,这种解决方案是可以工作的,但是也有一些值得注意的陷阱。

所有浏览器都将未知元素渲染成 inline 的,也就是给它们默认添加一个display:inline的 CSS 规则。

HTML5 定义了很多新的块级元素。也就是说,它们可以包含其他块级元素,支持 HTML5 的浏览器应该默认使用display:block进行渲染。如果你希望在旧的浏览器中使用这些元素,记得添加下面的 CSS 规则进行修正:

article,aside,details,figcaption,figure,
footer,header,hgroup,menu,nav,section {
    display:block;
}

(这个规则来自 Rich Clark 的 HTML5 Reset Stylesheet,你可以在这篇文章中找到很多有趣的东西。)

等等!这有点不对劲!在 IE9 之前的版本中,IE 不会为未知元素应用任何 CSS 样式。例如,如果你这么写:

<style type="text/css">
  article { display: block; border: 1px solid red }
</style>
...
<article>
<h1>Welcome to Initech</h1>
<p>This is your <span>first day</span>.</p>
</article>

Internet Explorer (IE8 及更早版本)不会把<article>当成块级元素,也不会在 article 外面加个 border。所有样式都会被忽略掉。直到 Internet Explorer 9 才修复这一问题。

第二个问题是,浏览器发现未知元素的时候,它们如何创建 DOM 对象。这次,问题最大的还是老版本的 Internet Explorer(IE9 同样修复了这个问题)。如果 IE8 不能识别元素的名字,它会向 DOM 中插入一个没有子元素的空元素。所有你期望作为该未知元素的直接子元素都成了其兄弟元素的子元素。

下面我们用 ASCII 字符图来看一下不同之处。例如,正常的 HTML 5 是这样子的:

article
|
+--h1 (child of article)
|  |
|  +--text node "Welcome to Initech"
|
+--p (child of article, sibling of h1)
   |
   +--text node "This is your "
   |
   +--span
   |  |
   |  +--text node "first day"
   |
   +--text node "."

但实际上 IE8 创建出来的是这样子的:

article (no children)
h1 (sibling of article)
|
+--text node "Welcome to Initech"
p (sibling of h1)
|
+--text node "This is your "
|
+--span
|  |
|  +--text node "first day"
|
+--text node "."

这个问题有一个神奇的解决方案。那就是,在你使用之前用 JavaScript 创建一个临时的 <article>元素。此时,Internet Explorer 会神奇地识别出<article>元素,然后让你添加 CSS 样式,根本不需要向 DOM 增加临时元素。简单地在每一个页面事先创建一个元素,就可以教给 IE 正确地为未知元素添加样式。也就是类似我们下面所写的代码:

<html>
<head>
<style>
  article { display: block; border: 1px solid red }
</style>
<script>document.createElement("article");</script>
</head>
<body>
<article>
<h1>Welcome to Initech</h1>
<p>This is your <span>first day</span>.</p>
</article>
</body>
</html>

这种投机取巧的技术能够在任何版本的 IE 中使用,甚至是 IE6!我们可以扩展一下这种技术,一次性为所有 HTML5 元素都创建一个临时拷贝,再一次强调,这些元素决不能插入到 DOM 中,因此你永远不会看到这些元素。然后把它们放在浏览器开始的位置,让老版本 IE 同样支持 HTML5。

Remy Sharp 已经为我们做好了这些准备。他把他的工作命名为 HTML5 使能脚本(HTML5 enabling script)。这段脚本在你读本文的时候已经更新过很多版本了,以下是其基本思想:

<!--[if lt IE 9]>
<script>
  var e = ("abbr,article,aside,audio,canvas,datalist,details," +
    "figure,footer,header,hgroup,mark,menu,meter,nav,output," +
    "progress,section,time,video").split(',');
  for (var i = 0; i < e.length; i++) {
    document.createElement(e[i]);
  }
</script>
<![endif]-->

<!--[if lt IE 9]><![endif]-->被称为条件注释。Internet Explorer 将其解释为 if 语句:“如果当前的 Internet Explorer 版本低于 9,则执行这段代码。”其他所有浏览器都会将其看做是普通的 HTML 注释。其结果就是,Internet Explorer(9 之前的版本)会执行这段代码,其他所有浏览器都会将其忽略。这种判断会让你的页面加载更快一些,因为这些浏览器不需要执行这种技巧性代码。

这段代码的 JavaScript 部分很直白。变量 e 是一系列诸如 "abbr"、"article"、"aside"等等的字符串数组。通过遍历这个数组,我们使用document.createElement()创建数组中每一个元素。但是我们忽略了这个函数的返回值,因为我们创建的元素不需要插入到 DOM 中。不过,这对于 Internet Explorer 已经足够,足以让我们在页面的后续部分正常使用这些元素。

“后续”一词非常重要。这段脚本应该在页面的顶部,最好还在<head>元素中,而不是在页面最后。这样的话,Internet Explorer 就可以在处理你的标签和属性之前先执行这段代码。如果你把它放在页面底部,显然已经太晚了。Internet Explorer 已经构建了错误的 DOM,错误地解析了你的标记。最后它执行了你的代码,但是它绝不会重新再渲染一遍页面。

Remy Sharp 将这段代码“压缩”之后托管在 Google Project Hosting 上。(正如你想象的那样,这段代码是基于 MIT 协议开源的,所以你可以自由地应用到你的项目中。)如果需要,你也可以直接链接到托管地址:

<head>
  <meta charset="utf-8" />
  <title>My Weblog</title>
  <!--[if lt IE 9]>
  <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  <![endif]-->
</head>

现在,我们已经有了足够的准备知识来使用 HTML5 提供的新的标签了!

发表评论

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2