详解IE7以下独有的hasLayout

目录
  1. 什么是hasLayout
  2. 默认情况下hasLayout=true的元素
  3. 怎么触发及清除 hasLayout
  4. hasLayout 的影响
  5. hasLayout 引起的bug
  6. 怎么检测IE下的某个元素是否拥有hasLayout
  7. 需要注意的是

什么是hasLayout

hasLayout property: Gets a value that indicates whether the object has layout.

hasLayout 是IE渲染引擎的一个内部实现. IE中, 一个元素要么自己计算大小组织内容(自己渲染), 要么依赖父元素来计算大小和组织内容(依赖祖先元素渲染). 为了区分两者, 渲染引擎采用了 hasLayout 属性, 该属性可以设置为 true 或 false. 若一个元素的 hasLayout 属性值为 true 时, 这个元素便拥有了一个布局(layout), 该元素便不在依赖某个祖先元素进行渲染, 而是它自己就去渲染自己了, 它会负责对自己和可能的子孙元素进行尺寸计算和定位, 这意味着这个元素需要花更多的代价来维护自身和里面的内容; 相反的, 若元素的 hasLayout 属性值为 false时, 它会直接依赖于某个祖先元素来完成这些工作, 最终造成很多的IE bugs.

默认情况下hasLayout=true的元素

下列元素默认拥有 layout:

  • html body
  • table tr th td
  • img
  • hr
  • input button file select textarea fieldset
  • marquee
  • frameset frame iframe
  • objects applets embed

怎么触发及清除 hasLayout

以下css样式的设置, 会触发元素的 hasLayout:

  • position: absolute(IE5+)
  • float: left|right(IE5+)
  • display: inline-block(IE5+)
  • width|height: “auto”以外的任何值(IE5+; 只对block元素有效)
  • zoom: “normal”以外的任何值(IE5.5+; IE私有属性)
  • writing-mode: tb-rl(IE5+; IE私有属性)
  • overflow: hidden|scroll|auto(IE7; 此属性在IE6及更早版本中不能应用在未触发hasLayout的元素上)
  • overflow-x|-y: hidden|scroll|auto(IE7; 此属性在IE6及更早版本中不触发hasLayout; 此属性在CSS3中才获支持)
  • position: fixed(IE7)
  • min-width: 任何值(IE7; 即使是0)
  • max-width: “none”以外的任何值(IE7)
  • min-height: 任何值(IE7)
  • max-height: “none”以外的任何值(IE7)
  • position: fixed(IE7)

以下css样式的设置, 会清除已经触发的 hasLayout:

  • position: static(IE5+)
  • float: none(IE5+)
  • display: “inline-block”以外的任何值(IE5+)
  • width|height: “auto”(IE5+; 对inline元素无效)
  • zoom: “normal”(IE5.5+; IE私有属性)
  • writing-mode: 从’tb-rl’到’lr-tb’(IE5+; IE私有属性)
  • max-width|max-height: “none”(IE7)
  • overflow: visible(IE7)

hasLayout 的影响

  1. 浮动元素可以被 layout 元素自动包含. 一般情况下, 由于浮动元素脱离普通文档流会造成父元素的坍塌. 但是在IE6-7下, 通过触发父元素的 hasLayout, 可以使得父元素自动包含浮动的子元素, 从而修复坍塌问题. 一般我们设置父元素的 *height:1%; 即可, 1%并不会改变实际高度, 只是触发了 hasLayout, 该方法被称为霍莉破解(Holly hack), 需要注意的是, 当这个元素的 overflow 属性被设置为 visible 时, 这个方法就失效了.
  2. 正常情况下, 浮动元素旁边的元素, 其内容应该环绕该浮动元素. 如果这个元素拥有 layout, 那么这个元素就会表现为一个矩形, 其内容不会环绕浮动元素.
  3. IE独有的滤镜属性(filter) 只适用于 layout 元素, 若一个div 设置了filter:alpha(opacity=90), 又没有触发该div 的 hasLayout, 那么透明的设置将无效.
  4. hasLayout 影响块级元素鼠标的响应区域, 通常 hasLayout=false时, 只有文字区域才有响应, 而 hasLayout=true 时, 整个块级元素都是可以响应的.

hasLayout 引起的bug

  1. IE6 及更低版本的双空白边浮动 bug, 修复方案: display:inline;
  2. IE5-6的 3 像素偏移 bug, 修复方案: _height:1%;
  3. IE6 的躲躲猫(peek-a-boo) bug, 修复方案: _height:1%;
  4. IE6-7负margin隐藏Bug, 修复方案: 去掉父元素的hasLayout; 或者赋hasLayout给子元素, 并添加position:relative;

怎么检测IE下的某个元素是否拥有hasLayout

在 IE Developer Toolbar 下, 拥有 haslayout的元素, 通常显示为 “haslayout = -1”. 也可通过js的方式检测, 如下所示:

var element = document.getElementById("myDiv");
console.log(element.currentStyle.hasLayout);//该方式只能获取值, 而不能设置

以下代码可用于在IE6-7下测试某个元素是否拥有hasLayout:

Code example: http://samples.msdn.microsoft.com/workshop/samples/author/dhtml/refs/hasLayout.htm

<!DOCTYPE html> 
<html> 
<head> 
  <title>hasLayout Property</title> 
</head> 

<body> 
  <h1>hasLayout Property</h1> 
  <p>This example uses the <strong>hasLayout</strong> property of the <strong>currentStyle</strong> object to  
    show that an element has layout when it is absolutely positioned, or when its height and/or width are specified.   
    The <strong>hasLayout</strong> property returns <strong>true</strong> for an object that has layout, and  
    <strong>false</strong> for an object that has no layout.</p> 
  <fieldset style="width: 50%; text-align: center;"> 
    <legend><strong>hasLayout</strong> Property</legend> 
    <p style="text-align: left;"><em>Which DIV element has layout?</em></p> 
    <div id="oWidthSet" style="width: 100%; text-align: left;"><strong>DIV</strong> element A has its <strong>width</strong> set to <strong>100%</strong>.</div> 
    <div id="oNotSet" style="text-align: left;"><strong>DIV</strong> element B is not positioned, and neither its <strong>height</strong> nor <strong>width</strong> is set.</div> 
    <br> 
    <button onclick="document.getElementById('messageBox').textContent = document.getElementById('oWidthSet').currentStyle.hasLayout;">DIV Element A</button> 
    <button onclick="document.getElementById('messageBox').textContent = document.getElementById('oNotSet').currentStyle.hasLayout;">DIV Element B</button> 
  </fieldset> 
  <div id="messageBox" style="padding-top: 1em; font-weight: bold;"></div> 
</body> 
</html>

另外, 若一个元素没有布局(layout), 那么IE下 clientWidth/clientHeight 总是返回0. 基于这点, 可以使用另一种js的方法检测元素是否拥有hasLayout, 如下:

console.log(element.clientHeight==0);//等于true则表示该元素不拥有hasLayout

需要注意的是

  • hasLayout 功能只存在于IE7及低版本的浏览器上, IE8中已删除 hasLayout 功能.
  • hasLayout 触发后, 没有办法直接设置 hasLayout=false, 只有将那些触发 hasLayout 的 css 属性去除, 才能恢复原样.

本文就讨论这么多内容,大家有什么问题或好的想法欢迎在下方参与留言和评论.

本文作者: louis

本文链接: http://louiszhai.github.io/2016/03/31/css-hasLayout/

参考文章

Fork me on GitHub