打印

HM8003: 各浏览器对 BODY 元素或通常被放置在 BODY 元素内的元素出现在第一个 FRAMESET 元素之前时所生成的文档树存在差异

作者:钱宝坤

标准参考

FRAME 元素介绍(Introduction to frames)

HTML FRAMES 可以将文档(HTML 页面)呈现在多个视口中,并且该文档(HTML 页面)可以成为一个独立的窗口或子窗口。当某些视口发生滚动或变化(页面内容改变或被重定向)时,多个视口提供给设计者保持其他视口的信息始终可见的一种方法。例如,在同一个窗口中,第一个 FRAME 可以用来呈现标题等静态信息,第二个 FRAME 可以用来呈现一个导航菜单,第三个 FRAME 可以用来呈现主题内容并且它可以被用户滚动或通过第二个窗口中的导航菜单被新页面替换掉,而这些发生在第三个 FRAME 中的变化是不会影响其他两个 FRAME 的。
请参考 HTML4.01:16.1 Introduction to frames

FRAMESET 元素介绍(Introduction to FRAMESET)

包含 FRAME 的 HTML 文档(又称:"frameset document")与不包含 FRAME 的文档有着不同的标记。一个标准的 HTML 文档包含一个 HEAD 部分和一个 BODY 部分,然而一个 "frameset document" 包含一个 HEAD 部分和一个 FRAMESET 部分。
文档的 FRAMESET 部分用来指定视口在客户端主窗口中的布局。另外,当客户端不支持 FRAME 或被配置为不显示 FRAME 时,FRAMESET 部分可以包含一个非 FRAME 元素作为替换内容。
通常被放置在 BODY 元素内的元素不能出现在第一个 FRAMESET 元素之前,否则 FRAMESET 元素将被忽略。
请参考 HTML4.01:16.2 Layout of frames

更多参考资料:
W3C HTML4.01:16.2.1 The FRAMESET element
W3C DOM-level_2:Interface HTMLFrameSetElement
关于 window.frames 集合资料,请参考各浏览器厂商相关文档: MSDNMozilla Developer CenterSafari Refernece Library

问题描述

各浏览器对 BODY 元素或通常被放置在 BODY 元素内的元素出现在第一个 FRAMESET 元素之前时,最终生成的文档树存在差异。

造成的影响

由于该问题会引起各浏览器中最后生成的文档树存在差异,最终会导致页面中其他元素的布局可能在各浏览器中的最终表现不同。

受影响的浏览器

所有浏览器  

问题分析

下面分别建立四个 HTML 页面,top.html、test.html、frame1.html、frame2.html,页面间的关系及主要内容和作用如下:

  • frame1.html、frame2.html 通过 frameset 标记嵌入在 test.html 中,frame1.html和frame2.html 为只有简单内容的两个普通页面,而 test.html 是为了触发本文所叙述的问题所设计的页面,其内容请见下面的代码部分;
  • test.html 通过 "iframe" 标记嵌入在 top.html 中,并在 top.html 中通过一个递归函数遍历出 test.html 中 HTML 结点下的结点树形结构;

top.html中的代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style>
body{font:'arial' 16px/1.5;}
</style>
<script>

window.onload=function(){
  var tree = "",
      string = "";

  function build(){
    var htmlElement = window.frames["ifr"].document.documentElement;
    traverse(htmlElement,0);
    document.getElementById("info").innerHTML += tree;

  }

  function traverse(element,level){
    var tabs;
    if (!element.children.length){
      tabs = "└";
      tree += new Array(level + 1).join('&nbsp;&nbsp;&nbsp;') + tabs + element.tagName + "<br />";
      return;
    }else{
      tabs = "+";
      tree += new Array(level + 1).join('--') + tabs + element.tagName + "<br />";
      level++;
    }
    for (var i = 0, l = element.children.length; i < l; i++){
      traverse( element.children[i] ,level );
    }
  }
  build();
}
</script>
</head>
<body>
<iframe name="ifr" src="test.html">
</iframe>
<div id="info">
test.html中 HTML 结点下的结点树形结构如下:<br/>
</div>
</body>
</html>

test.html中的代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style>
  * { font-family: 'Lucida Console' }
</style>
</head>
<form><div><input type="hidden" value="test"/></div>
<p><span></span></p>
</form>
<frameset cols="40%, 60%">
  <frame src="frame1.html" />
  <frame src="frame2.html" />
</frameset>
</html>

test.html 中 HTML 的结点树形结构如下:

IE6 IE7(S) IE8(S) IE7(Q) IE8(Q)1 FireFox
+HTML
---+HEAD
      └TITLE
      └META
      └STYLE
------+FORM
         └INPUT
---+FRAMESET
      └FRAME
      └FRAME
---+BODY
      └DIV
------+P
         └SPAN
+HTML
---+HEAD
      └TITLE
      └META
      └STYLE
------+FORM
         └INPUT
---+FRAMESET
      └FRAME
      └FRAME
+HTML
---+HEAD
      └META
      └TITLE
      └STYLE
---+FRAMESET
      └FRAME
      └FRAME
          
Opera1 Safari1 Chrome
+HTML
---+HEAD
      └META
      └TITLE
      └STYLE
---+BODY
------+FORM
---------+DIV
           └INPUT
---------+P
           └SPAN
------+FRAMESET
         └FRAME
         └FRAME
          
+HTML
---+HEAD
    └META
    └TITLE
    └STYLE
---+BODY
------+FORM
---------+DIV
        └INPUT
---------+P
        └SPAN
---+FRAMESET
    └FRAME
    └FRAME
          
+HTML
---+HEAD
      └META
      └TITLE
      └STYLE
---+BODY
------+FORM
---------+DIV
            └INPUT
---------+P
            └SPAN
          

注【1】:Opera 和 Safari 中的差异在于 FRAMESET 结点在文档树中所处的位置不同。Opera 中属于 BODY 的子元素,Safari 中属于 HTML 的子元素。

各浏览器中产生差异的主要原因为:在一个页面中,当 BODY 元素或通常被放置在 BODY 元素内的元素出现在第一个 FRAMESET 元素之前时,各浏览器是按照 "HTML Document" 还是按照 "frameset document" 去生成最终文档树所产生的。从运行结果中可以看到在 IE7(Q) IE8(Q) Opera Safari Chrome 生成的文档树中加入了 BODY 结点,这是各浏览器的修复功能所产生的,根据规范中的描述(通常被放置在 BODY 元素内的元素不能出现在第一个 FRAMESET 元素之前,否则 FRAMESET 元素将被忽略。)可知,只有在 Chrome 中符合规范的描述。

读者也可以将此测试用例在不同浏览器中运行后使用各浏览器特有的调试工具查看最终生成的文档树结构。

解决方案

针对此问题在各浏览器中产生的差异,要看设计者的最终目的是什么,但始终应该参照规范中的描述使得 BODY 元素或通常被放置在 BODY 元素内的元素不能出现在第一个 FRAMESET 元素之前。

参见

知识库

相关问题

测试环境

操作系统版本: Windows 7 Ultimate build 7600
浏览器版本: IE6
IE7
IE8
Firefox 3.6.11
8.0.552.5 dev
Safari 5.0.2
Opera 10.63
测试页面: top.html
本文更新时间: 2010-10-20

关键字

FRAME FRAMESET BODY