没有<html>、<head>、<body>的页面

今天在学习DIVE INTO HTML5时,在HTML5 Peeks, Pokes and Pointers一页看到这样一句话:

Optional start tags. <html>, <head>, <body>, <tbody>, <colgroup>. Amaze your friends! Skip the tags and still validate!

顿时来了兴趣。

其实这一点在WHATWGHTML vs. XHTML页面中也有提到,其中最常接触的就是<tbody>标签的省略(其实多数是因为偷懒或者干脆是忘记了),部分浏览器(IE)会自动补上一个<tbody>元素,而其他浏览器则可以在没有<tbody>的情况下渲染<table>(此处确实是IE的错,因为标准里说<table>里允许直接写<tr>,这点和本文要说的<html><head><body>标签不同)。

既然<html><head><body>这三个标签可以省略,但是标准中指定的根元素<html>元素,且在<html>元素的内容模型中明确其只能包含一个<head>元素和一个<body>元素,甚至连<head>必须在<body>之前也已经明确说明了。那么这就意味着,虽然在编写过程中,HTML里没有<html><head><body>这三个元素,但是在浏览器对纯文本的文档进行解析之后,势必要按着标准来创建这三个元素,最终的DOM模型里必须有<html><head><body>存在。

于是就会产生一个问题,如果完全不写这三个元素,那么浏览器会根据什么样的原理,将所有的元素分别放置到自动生成的head和body元素之中。为此,编写了一个页面,通过一个简单的例子,试图找出浏览器自动创建head和body元素的规律。

页面可以通过这里查看测试页面,页面的html结构如下:

  • DOCTYPE
  • meta
  • title
  • style
  • script
  • h1
  • p
    • q
    • code
  • p
    • code
  • script
  • pre
    • code

在各浏览器中测试结果如下:

各浏览器结果

各浏览器中的结果出奇地一致,就连老得掉渣的IE6也很顺应主流地将各个元素“正确”地分配到了<head><body>元素之中。

经过对各浏览器中的结果的观察,似乎不难得出这样的一个结论:浏览器始终创建一个head元素,并自上而下依次检查页面源码中的各标签,能够加入到head元素的标签都加入到head元素中,随后将剩余的元素分配到自动创建的body元素中。

而这种结果也是符合正常的思维逻辑的,可见浏览器确实可以为我们做到很多,让我们省心省力……

那么进一步的,<head>元素中可以放置哪些元素呢?在标准中的head标签一章中明确指出,<head>元素的内容模型为元数据内容,包括了<base><command><link><meta><noscript><script><style><title>这几个。

因此,对于自动创建<head><body>元素这一回事,如果使用代码来表述,大概是以下的模式:

// elements - 所有顶层元素
var head = document.createElement('head');  
var body = document.createElement('body');  
var forceToBody = false; //指定所有元素都放到body中

for (var i = 0; i < elements.length; i++) {  
    var element = elements[i];
    if (!forceToBody && isMetadataContent(element)) {
        head.appendChild(element);
    }
    else {
        !foreceToBody && forceToBody = true;
        body.appendChild(element);
    }
}
document.appendChild(head);  
document.appendChild(body);  

其实,<html><head><body>这三个标签确实没什么用,<html>还有一个lang属性,如果需要用到这个属性的话,就留着html,其他时候完全就是一个累赘,反而导致HTML的结构多了一层,看起来更复杂了。个人的推荐是,从此去掉<html><head><body>三个标签,在<head>内容和<body>内容之间使用2-3个换行来分隔,整个源码会更清爽整洁。

题外话:在群里说起这个问题的时候,我先表达的是“没有<html><head><body>元素的HTML页面能通过验证器验证”,结果遭到了反驳,大家提出不要为了验证器而编码。其实确实是这样的,个人的观点一直是“只要浏览器表现一致,且稳定的标准支持这种使用方法,那么就可以投入生产环境之中”,对于前端这样多变、分裂的局面,我想这种态度相对一本正经地抱着标准不放会来得更好。

作为扩展阅读,可以看一下这篇文章,以<script>标签为例,详细讲述了各浏览器的标签补全行为,非常值得参考。