打印

BW5002: WebKit 中依赖文本流定位的绝对定位元素某些情况下不会更新其位置

作者:孙东国

标准参考

当一个绝对定位的元素的 'top' 'right' 'bottom' 'left' 为 'auto' 时,绝对定位的元素可能会被放置在其“静态位置”上。

“静态位置”粗略地说是指元素如果在正常流中的位置。具体来说:“静态位置”的包含块是若一个元素的 'position' 为 'static' 'float' 为 'none' 时生成元素的第一个框的假想框。

关于“静态位置”的详细信息,请参考 CSS 2.1 规范 static position 中的内容。

关于绝对定位元素放置位置的计算方式,请参考 CSS 2.1 规范 10.3.7 Absolutely positioned, non-replaced elements 中的内容。

问题描述

WebKit 中依赖文本流定位的绝对定位元素在其“静态位置”改变后不会变更其位置。

造成的影响

该问题将导致页面布局在 Chrome Safari 中的某些情况下与预期不符。

受影响的浏览器

Chrome Safari  

问题分析

1. 通过修改容器的 scrollLeft/scrollTop 值测试依赖该容器内文本流定位的绝对定位元素是否会更新其位置

分析以下代码:

<script type="text/javascript">
function $(id){
  return document.getElementById(id);
}
function toggle($target){
  $target.style.display=$target.offsetWidth?"none":"block";
}
</script>
<div id="container_1" style="width:300px; height:300px; margin:0 auto; border:3px solid gold; overflow:scroll;">
  <div style="width:600px; height:600px; padding:50px; background:silver;">
    <div style="width:80px; height:80px; background:blue;"></div>
    <div id="absolutely_1" style="position:absolute; background:red; width:100px; height:100px;">absolute</div>
  </div>
</div>
<div style="width:300px; margin:0 auto;">
  <button style="height: 100px; float:right;" onclick="toggle($('absolutely_1'));">show/hide Red</button>
  <button onclick="$('container_1').scrollLeft=0;">Silver.scrollLeft=0</button>
  <button onclick="$('container_1').scrollLeft=30;">Silver.scrollLeft=30</button>
  <button onclick="$('container_1').scrollTop=0;">Silver.scrollTop=0</button>
  <button onclick="$('container_1').scrollTop=30;">Silver.scrollTop=30</button>
</div>

样例代码中有依赖文本流定位的绝对定位元素 absolutely_1(即其 'top' 'right' 'bottom' 'left' 均为 'auto')。

在更改其容器的 scrollLeft/scrollTop 值时:

  • IE6(Q) IE7(Q) IE8(Q) 会实时更新 absolutely_1 的位置,其他浏览器则不会。
  • window.onresize 被触发时,IE8(S) Firefox 会更新 absolutely_1 的位置。
  • absolutely_1 的 'display' 设置为 'none' 再重设为 'block' 时,IE6(S) IE7(S) Firefox 会更新 absolutely_1 的位置,IE8(S) Chrome Safari Opera(S) 不会更新,Opera(Q) 出现渲染错误。

2 通过修改容器的 marginLeft/marginTop 值测试依赖该容器内文本流定位的绝对定位元素是否会更新其位置

分析以下代码:

<script type="text/javascript">
function $(id){
  return document.getElementById(id);
}
function toggle($target){
  $target.style.display=$target.offsetWidth?"none":"block";
}
</script>
<div style="width:300px; height:300px; margin:0 auto; border:3px solid gold; overflow:hidden;">
  <div id="container_2" style="width:600px; height:600px; padding:50px; background:silver;">
    <div style="width:80px; height:80px; background:blue;"></div>
    <div id="absolutely_2" style="position:absolute; background:red; width:100px; height:100px;"></div>
  </div>
</div>
<div style="width:400px; margin:0 auto;">
  <button style="height: 100px; float:right;" onclick="toggle($('absolutely_2'));">show/hide Red</button>
  <button onclick="$('container_2').style.marginLeft='0';">Silver.style.marginLeft='0'</button>
  <button onclick="$('container_2').style.marginLeft='-30px';">Silver.style.marginLeft='-30px'</button>
  <button onclick="$('container_2').style.marginTop='0';">Silver.style.marginTop='0'</button>
  <button onclick="$('container_2').style.marginTop='-30px';">Silver.style.marginTop='-30px'</button>
</div>

样例代码中有依赖文本流定位的绝对定位元素 absolutely_2(即其 'top' 'right' 'bottom' 'left' 均为 'auto')。

在更改其容器的 'margin' 值时:

  • IE6 IE7 IE8 Firefox Opera Safari 会实时更新 absolutely_2 的位置。
  • absolutely_2 的 'display' 设置为 'none' 再重设为 'block' 时,Chrome 会更新 absolutely_2 的位置。

3. 总结

依赖文本流定位的绝对定位元素在其“静态位置”发生变更后,在不同浏览器中的位置变化情况不同。

解决方案

在绝对定位元素的包含块可能发生改变时(即该绝对定位元素的“静态位置”可能发生变化时),不要让该绝对定位元素依赖其“静态位置”来定位,给该元素设定明确的 left、right、top 或 bottom 值。

参见

知识库

相关问题

测试环境

操作系统版本: Windows 7 Ultimate build 7600
浏览器版本: IE6
IE7
IE8
Firefox 3.6
Chrome 4.0.302.3 dev
Safari 4.0.4
Opera 10.51
测试页面: position_refresh.html
本文更新时间: 2010-07-16

关键字

position absolute 文档流 位置 更新