关于 hasLayout-CSS属性

作者:北大青鸟 
原文地址:http://www.bjaccpedu.com/xsxt_end.php?bsort=8&ssort=52&id=3884

转载请注明出处!


Internet Explorer 中有很多奇怪的渲染问题可以通过赋予其“layout”得到解决。John Gallant 和 Holly Bergevin 把这些问题归类为“尺寸bug(dimensional bugs)”,意思是这些 bug 可以通过赋予相应元素某个宽度或高度解决。这便引出关于“layout”的一个问题:为什么它会改变元素的渲染特性,为什么它会影响到元素之间的关系?这个问题问得很好,但却很难回答。在这篇文章中,我们专注于这个复杂问题会有那些方面的表现,某一方面的具体讨论和范例请参考文中给出的相关链接。

hasLayout ? 定义
“Layout”是一个 IE/Win 的私有概念,它决定了一个元素如何显示以及约束其包含的内容、如何与其他元素交互和建立联系、如何响应和传递应用程序事件/用户事件等,这有点类似于一个窗体的概念。

微软的开发者们认为盒状元素(box-type elements)应该具有一个“属性(property)”(这是面向对象编程中的一个概念),于是他们便使用了 layout , 也就是 hasLayout。

hasLayout 其实既不是一个属性更不是一个行为,而是 IE 这个渲染引擎代代继承一贯拥有的一个渲染概念,在这个概念下渲染的元素将具有一种特性。

实际上这种渲染特性在有些 HTML 元素中与身俱来,而在另外一些元素中也可以通过一些 CSS 属性将其触发为 true ,且一旦触发将不可逆转。

术语
当我们说一个元素“拥有layout”或“得到layout”,或者说一个元素“has layout” 的时候,我们的意思是指它的微软专有属性 hasLayout 被设为了 true 。一个“layout元素”可以是一个默认就拥有 layout 的元素或者是一个通过设置某些 CSS 属性得到 layout 的元素。

而“无layout元素”,是指 hasLayout 未被触发的元素,比如一个未设定宽高尺寸的干净 div 元素就可以做为一个 “无layout祖先”。

给一个默认没有 layout 的元素赋予 layout 的方法包括设置可触发 hasLayout = true 的 CSS 属性。参考默认 layout 元素以及这些属性列表。没有办法设置 hasLayout = false , 除非把一开始那些触发 hasLayout = true 的 CSS 属性去除。

问题种种
hasLayout 的问题不管新手还是老手,不管设计师或者程序员可能都遇到过。具有 layout 的元素通常有着不同寻常而且难以预料的的显示效果,而且有时甚至会牵连到他们的孩子元素。

一个元素是否具有“layout”可能会引发如下的一些问题:

IE 很多常见的浮动 bug 。 
元素本身对一些基本属性的异常处理问题。 
容器和其子孙之间的边距重叠(margin collapsing)问题。 
使用列表时遇到的诸多问题。 
背景图像的定位偏差问题。 
使用脚本时遇到的浏览器之间处理不一致的问题。 
上面的列表只是列出一个大概,也不完善。下面的文章将尽可能详细彻底的描述有无“layout”所带来的各种问题。

Layout 从何而来
不同于标准属性,也不像某些浏览器的私有 CSS 属性,layout 无法通过某一个 CSS 声明直接设定 。也就是说没有“layout属性”这么一个东西,元素要么本身自动拥有 layout,要么借助一些 CSS 声明悄悄地获得 layout。

默认layout元素
下列元素应该是默认具有 layout 的:

<html>, <body> 
<table>, <tr>, <th>, <td> 
<img> 
<hr> 
<input>, <select>, <textarea>, <button> 
<iframe>, <embed>, <object>, <applet> 
<marquee> 
属性
下列 CSS 属性和取值将会让一个元素获得 layout:

position: absolute 
绝对定位元素的包含区块(containing block)就会经常在这一方面出问题。 
float: left|right 
由于 layout 元素的特性,浮动模型会有很多怪异的表现。 
display: inline-block 
当一个内联级别的元素需要 layout 的时候往往就要用到它,这也可能也是这个 CSS 属性的唯一效果??让某个元素拥有 layout。“inline-block行为”在IE中是可以实现的,但是非常与众不同: IE/Win: inline-block and hasLayout 。 
width: 任意值 
很多人遇到 layout 相关问题发生时,一般都会先尝试用这个来修复。 
height: 任意值 
height: 1% 就在 Holly Hack 中用到。 
zoom: 任意值 (MSDN) 
MS专有属性,无法通过校验。 不过 zoom: 1 可以临时用做调试。 
writing-mode: tb-rl (MSDN) 
MS专有属性,无法通过校验。 
在 IE7 中,overflow 也变成了一个 layout 触发器:

overflow: hidden|scroll|auto 
这个属性在之前版本 IE 中没有触发 layout 的功能。 
overflow-x|-y: hidden|scroll|auto 
overflow-x 和 overflow-y 是 CSS3 盒模型中的属性,尚未得到浏览器的广泛支持。他们在之前版本IE中没有触发 layout 的功能。 
另外 IE7 的荧幕上又新添了几个 haslayout 的演员,如果只从 hasLayout 这个方面考虑,min/max 和 width/height 的表现类似,position 的 fixed 和 absolute 也是一模一样。

position: fixed 
./. 
min-width: 任意值 
就算设为0也可以让该元素获得 layout。 
max-width: 除 none 之外的任意值 
./. 
min-height: 任意值 
即使设为0也可以让该元素的 haslayout=true 
max-height: 除 none 之外的任意值 
./. 
以上结论借助 IE Developer Toobar 以及预先测试得出。

有关内联级别元素
对于内联元素(可以是默认即为内联的比如 span 元素,也可以是 display: inline 的元素)

width 和 height 只在 IE5.x 下和 IE6 的 quirks 模式下触发 hasLayout 。因为在 IE6 中,如果浏览器运行于标准兼容模式下,内联元素会忽略 width 或 height 属性,所以设置 width 或 height 不能在此种情况下令该元素具有 layout。 
zoom 总是可以触发 hasLayout,但是在 IE5.0 中不支持。 
具有“layout” 的元素如果同时也 display: inline ,那么它的行为就和标准中所说的 inline-block 很类似了:在段落中和普通文字一样在水平方向和连续排列,受 vertical-align 影响,并且大小可以根据内容自适应调整。这也可以解释为什么单单在 IE/Win 中内联元素可以包含块级元素而少出问题,因为在别的浏览器中 display: inline 就是内联,不像 IE/Win 一旦内联元素拥有 layout 还会变成 inline-block。

脚本属性 hasLayout
我们这里称 hasLayout 为“脚本属性”是为了和我们熟知的 CSS 属性相区别。

注意一旦一个元素拥有了 layout,就没有办法再将其设成 hasLayout = False 了。

hasLayout-property 可以用来检测一个元素是否拥有 layout:举个例子,如果它的 id 是“eid”,那么只要在 IE5.5+ 的地址栏里输入 javascript: alert(eid.currentStyle.hasLayout) 即可检测它的状态。

IE的 Developer Toolbar 可以实时检查一个元素的当前样式;如果 hasLayout 是 true ,那么它的值显示为 “-1”。 我们可以通过实时修改一个元素的属性将“zoom(css)”设置为“1”来触发 hasLayout 以便调试。

另外一个需要注意的是“layout”会影响脚本编程。如果一个元素没有“layout”,那么clientWidth/clientHeight 总是返回0。这会让一些脚本新手感到困惑,而且这和 Mozilla 浏览器的处理方式也不一样。不过我们可以利用这一点在 IE5.0 中检测“layout”:如果 clientWidth 是零那么这个元素就没有 layout。

CSS hacks
下面用于触发 haslayout 的 hack 已经经过 IE6 及以下版本测试。今后版本的IE有可能会对此做不同处理。如果新版本浏览器发布我们会重新整理这部分内容。

John Gallant 和 Holly Bergevin 在2003年发布的 Holly hack :

/* */
* html .gainlayout { height: 1%; }
/* */   可以让 IE5+ 的任意元素获得 layout,除了标准兼容模式 IE6 中的内联元素。 
一般都很有效,除了在某些极少情况下,需要用 height:0 或者 1px 更好一些。 
和 overflow: hidden 不相容,除非在 IE6 的标注兼容模式下(因为这时如果父元素没有定高,那么height: 1% 会被变回 height: auto)。 
或者我们可以用 underscore hack:

.gainlayout { _height: 0; }另外,更具有向后兼容性的方法是使用 条件注释(conditional comments):

<!--[if lte IE 6]>
<style>
.gainlayout { height: 1px; }
</style>
<![endif]-->在条件注释中链接一个专门对 IE/Win 做修正的外部样式表文件,也不失为一个安全有效的好方法:

<link rel="stylesheet" href="allbrowsers.css" type="text/css" />
  
<!--[if lte IE 6]>
<link rel="stylesheet" href="iefix.css" type="text/css" />
<![endif]-->我们更倾向于使用 height: 0 和 1px ?? 并主张始终使用 height 除非它和别的什么东西冲突 (overflow: hidden)。对于取值,我们则倾向于避免 1% ,因为它可能会(虽然很少)引起一些问题。

一个需要注意的情况是如果我们希望一个元素保持内联,那么就不能使用 height 了,这时可以用 display: inline-block 。我们只在早期调试阶段用 zoom: 1 来避免一些渲染错误。

我们曾看过一些把 Holly hack 真的当作 holy(神圣的) hack 盲目使用的情况,比如对浮动元素使用或者对已经具有特定宽度的元素也使用这个 hack。要记住这个 hack 的目的不是要给某个元素加一个高度,而只是要触发 hasLayout = True 而已。

不要给所有元素设置 layout:* {_height: 1px;}。所谓过犹不及,获得 layout 不等于获得灵丹妙药,它只是用来改变渲染模式。

Hack整理
但是浏览器总是会变的,我们需要面对很多问题,比如一些依赖 IE6 的 bug 所做的 hack 会在 IE7 或更高版本的新浏览器中因 bug 修复而失效(甚至有害)的问题;比如新版本浏览器中类似的布局 bug 依然存在但用于 hack 的过滤器比如 * html 却不能正常工作的问题。这种情况下,MS专有属性 zoom 就可以考虑使用了。

<!--[if lt IE 7]><style>
/* IE 6 + IE5.5 + IE5.0 所用样式*/
.gainlayout { height: 0; }
</style><![endif]-->
  
<!--[if IE 7]><style>
.gainlayout { zoom: 1;}
/* 或者其他任何以后可能需要的东西 */
</style><![endif]-->zoom: 1; 可以让 IE5.5+ 的任何元素(包括内联元素)获得 layout,但是在 IE5.0 中无效。 
没有其他附带效果(内联元素会变成 inline-block,这个当然)。 
如果需要通过验证,应该用条件注释将 zoom 隐藏起来。 
其实当我们考虑到“向后兼容”时是很自相矛盾的,我们强烈建议页面设计者回过头看一下自己页面中用的到的明显的或是不明显的“hacks”,并用条件注释针对不同浏览器重新处理以保万无一失。

关于IE Mac 的小问题
IE Mac 和 windows 下的 IE 是完全不同的两个东西,它们各自拥有自己的渲染引擎,IE Mac 就全然不知“hasLayout”(或contenteditable)所谓何物。相比之下 IE Mac 的渲染引擎要更标准兼容一点,比如 height 就是被当作 height 处理,没有别的效果。因此针对“hasLayout”的 hacks 和别的解决方法(特别是通过使用 height 或 width 属性的)往往对 IE Mac 来说是有害的,所以需要对其隐藏。更多的关于 IE Mac 相关的问题可以在 IE Mac, bugs and oddities pages 找到。

MSDN 文档
MSDN 中涉及到 hasLayout 这个 MS 属性的地方寥寥无几,而具体解释 layout 和 IE 渲染模型之间关系的则少之又少。

在IE4的时候,除了未经绝对定位也未指定宽高的内联元素,几乎所有元素都有某种 layout(MSDN)。在这种早期的layout概念中,像 border, margin, padding 这些属性被称作“layout属性”,它们是不能应用到一个简单的内联元素上的。换句话说,“拥有layout”就可以粗略理解成:“可以拥有这几个属性”。

MSDN 上仍然使用 layout 属性这种说法, 只是含义变了,它们和拥有 layout 的元素已经没有什么关系了。在 IE5.5 中方才引入了 MS 的这个专有属性 hasLayout,也只是某种内部的标志位而已。

在 IE5.5 中,MSHTML Editing Platform(即可以通过设置<body contenteditable=true>来允许用户实时编辑、拖动 layout 元素以及调整其尺寸等)的文档中描述了三个和 layout 相关的重要特性:

如果一个 layout 元素中有内容,内容的排版布局将由它的边界矩形框决定。

拥有 layout 的意思基本上就是表示某元素是一个矩形。

从内部来说,拥有 layout 意思就是一个元素将自己负责绘制其内部内容。

(Editing Platform)

和 layout 自身相关的内部工作机制直到2005年8月才有相应文档描述,当时由于 The Web Standards Project 和微软的特别工作小组的原因,Markus Mielke [MSFT] 打开了深入讨论的大门:

一般来说,在 Internet Explorer 的 DHTML 引擎中,元素是不对自己的位置安排负责的。虽然一个 div 或者一个 p 元素都在源码中有一个位置,在文档流有一个位置,但是它们的内容却是由它们最近的一个 layout 祖先(经常是 body)控制安排的。这些元素依赖它们祖先的 layout 来为他们处理诸如决定大小尺寸和测量信息等诸多繁重的工作。

(HasLayout概述)

分析
我们的分析试图解释在已知案例下发生了什么事情,这种分析也应该可以作为未知案例下的指导。但我们这种试图利用种种测试案例投石探路的黑箱测试方法,是注定无法消除黑箱的神秘感的??我们无法回答“为什么”的问题。我们只能去尝试了解整个“hasLayout”模式的工作框架,以及它会怎样影响网页文档的渲染。因此,最终我们只能提供一些指导方针(而且只能是指导方针,而不是绝对的解决方案)。

我们认为他们所指的是一个小窗体。一个 layout 元素的内部内容是完全独立的,而且也无法影响其边界外的任何内容。

而 MS 属性 layout 只是某种标志位:一旦它被设定,这个元素就会拥有其特殊的 layout“特性”,这包括体现在其自身以及其非 layout 孩子身上的浮动、清除浮动、层叠、计数等等诸多方面的特殊性能。

这种独立性也许正可以解释为什么 layout 元素通常比较稳定,而且它们可以让某些 bug 消失。这种情况的代价有二,一是偏离了标准,二是它没有考虑到今后可能因此出现的 bug 和问题。

MS 的“页面”模式,从符号学角度考虑,可以看做是由很多互不相关的小的区块构成,而 HTML 和 W3C 的模式则认为“页面”模式应该是叙述完备的,故事性的相关信息区块构成的。

各种情况的详细说明
清除浮动和自动扩展适应高度
浮动元素会被 layou 元素自动包含。这是很多新手经常遇到的问题:在 IE 下完成的页面到了标准兼容浏览器下所有未清除的浮动元素都伸出了其包含容器之外。

Containing Floats 
how to clear floats without structural markup 
相反的情况:如果确实需要一个浮动元素伸出其包含容器,也就是自动包含不是想要的效果时,该怎么办?你很可能也会遇到这种头疼的问题,下面的深入讨论就是一个例子:

acidic float tests 
在IE中,一个浮动元素总是“隶属于”它的 layout 包含容器。而后面的元素会受这个 layout 包含容器影响而不是这个浮动元素影响。

这个特性和IE6的那个自动扩展以适应内部内容宽度的特性,都可以看成是受这个规则影响的:“由它的边界矩形框决定”。

更糟的问题:clear 无法影响其 layout 包含容器之外的 float 元素。如果依赖这个 bug 在 IE 中布局的页面要转到标准兼容浏览器中,只有全部重做。

更多相关信息查看本文 “和 CSS 规范类似的地方” 这一部分。

浮动元素旁边的元素
当一个块级元素紧跟在一个左浮动元素之后时,其中的文字内容应该沿着浮动元素的右边顺序排列并会滑到浮动元素下方。但是如果这个块级元素有 layout,比如由于某种原因被设置了宽度,那么这个 layout 元素就会表现为一个矩形,其中文字不会滑向浮动元素下方。其宽度也被错误计算??从浮动元素的右边开始算起了,所以如果给它设置 width: 100% 将会导致显示时这个 block 的宽度加上了浮动元素的宽度而出现横向滚动条。这种表现就和规范中描述的相去甚远了。

与此类似,和浮动元素相邻的相对定位元素,它的位置偏移量应该参照的是父元素的补白(padding)边缘(例如,left: 0; 应该将一个相对定位元素叠放于它前面的浮动元素之上)。在IE中,偏移量 left: value; 是从浮动元素的右边距(margin)边缘开始算起的,这会因浮动元素所占的宽度变化导致水平方向的错位(一个解决方法是用 margin-left 代替,但是也要注意如使用百分值时会有一些怪异问题)。

根据规范所述,浮动元素应该与其后的盒子交织在一起。而对于没有交叉的二维空间中的矩形而言这是无法实现的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值