IE雙倍邊距BUG 觸發 解決方案

jiestyle21發表於2012-03-15

標準參考

根據 W3C CSS2.1 規範中的描述,對於非替換的浮動元素,若 'margin-left' 或 'margin-right' 特性的計算值為 'auto',則它們的實際使用值為 '0'。

除此之外,'margin-left' 與 'margin-right' 特性的計算則採用其自身定義的規範。

關於 'margin-left'、'margin-right' 以及 非替換的浮動元素寬度計算 的詳細資訊,請參考 CSS2.1 規範 8.3 Margin properties: 'margin-top', 'margin-right', 'margin-bottom', 'margin-left', and 'margin' 以及 10.3.5 Floating, non-replaced elements 中的內容。

問題描述

在 IE5.0 IE5.5 IE6 中,當為一個塊級元素同時設定了向左浮動(float:left)及左邊距或右邊距('margin-left' | 'margin-right')後,則該元素的左邊距或右邊距在某些情況下會是設定值的兩倍。同樣地,向右浮動(float:right)及右邊距('margin-right')也存在此現象。

這個是 IE 著名的 "雙邊距Bug"(IE Double Margin Bug)。

造成的影響

這種雙倍邊距的怪異現象會對頁面造成很多影響,如意外折行、溢位、文字重疊等諸多相容性問題。

受影響的瀏覽器

IE5.0 IE5.5 IE6 

問題分析

首先重現這個 Bug。

分析以下程式碼:

<!DOCTYPE html>
<html>
<head>
<script>
  window.onload = function () {
    document.getElementById("d1").innerHTML = document.getElementById("d1").parentNode.offsetWidth;
    document.getElementById("d2").innerHTML = document.getElementById("d2").parentNode.offsetWidth;
  }
</script>
</head>
<body style="font:12px Arial; margin:0;">
<div style="width:100px;">
  <div style="background:gold; float:left;">
    <div id="d1" style="float:left; margin:0 50px; background:olive; width:100px; height:50px;"></div>
  </div>
  <div style="background:pink; float:left;">
    <div id="d2" style="float:right; margin:0 50px; background:deeppink; width:100px; height:50px;"></div>
  </div>
</div>
</body>
</html>

上面程式碼中有兩組 DIV 容器,容器內的 DIV 元素分別設定了向左浮動(float:left)與向右浮動(float:right),且左右邊距均為 50px(margin:0 50px)。當頁面載入完畢後將 DIV 容器的 offsetWidth 顯示出來。

在各瀏覽器中開啟這個頁面效果如下:

IE5.0 IE5.5 IE6 IE7 IE8 Firefox Chrome Safari Opera
IE5-6 double margins non IE5-6 double margins

從上面的例子與截圖可見:

  • 在 IE5.0 IE5.5 IE6 中,當一個塊級元素向左浮動時,其左邊距會出現雙倍於設定的邊距值的現象。當一個塊級元素向右浮動時,其右邊距會出現雙倍於設定的邊距值的現象。由於深黃色的塊級元素為其容器內的最後一個左浮動元素,所以其右邊距也會出現雙倍於設定的右邊距值的現象;
  • 在 其他瀏覽器 中,沒有上述的現象,瀏覽器遵照 W3C 規範對頁面元素進行解釋及渲染。

觸發此 Bug 的條件有 3 個:

  1. 若一個元素向左浮動(float:left),且其設定的左邊距('margin-left')大於其至容器的左側內邊界的距離:
    該元素實際的左邊距 = 設定的左邊距 * 2 - 左邊界至容器的距離;
  2. 同樣地,若一個元素向右浮動(float:right),且其設定的右邊距 ('margin-right')大於其至容器的右側內邊界的距離:
    該元素實際的右邊距 = 設定的右邊距 * 2 - 右邊界至容器的距離;
  3. 若一個元素向左浮動(float:left),這個元素為其父容器的最後一個左浮動元素,且其設定的右邊距('margin-right')大於其至容器的右側內邊界的距離:
    該元素的實際右邊距 = 設定的右邊距 * 2。

下面結合上面的觸發條件看一組更加複雜的例子:

<style>
  .fl { float:left; height:30px; background:gray; }
  #A { width:90px; margin:0 10px; }
  #B { width:85px; margin-left:150px; }
  #C { width:100px; margin-left:100px; }
</style>
<div style="width:600px; height:30px; background:#CCC;">
  <div id="A" class="fl">10 90 10</div>
  <div id="B" class="fl">150 85 0</div>
  <div id="C" class="fl">100 100 0</div>
</div>

測試程式碼中 DIV 容器中包含了三個 DIV 子元素,這三個子元素均為左浮動元素,且均擁有 'margin-left' 特性。

不同的瀏覽器執行的結果列表如下:

IE5.0
IE5.5
IE6
IE5-6 double margins
IE7
IE8
Firefox
Chrome
Safari
Opera
non IE5-6 double margins

在 IE5.0 IE5.5 IE6 中,

  • 【A】的左邊距容器的距離為 0,其設定的左邊距為 10px,10 > 0。則【A】滿足上面的條件1,觸發此 Bug。【A】實際的左邊距變為 10 * 2 - 0 = 20px。
  • 【B】的左邊距容器的距離為 120px(20 + 90 + 10),其設定的左邊距為 150px,150 > 120。則【B】滿足上面的條件1,觸發此 Bug。【B】實際的左邊距變為150 * 2 - 120 = 180px。
  • 【C】的左邊距容器的距離為 385px(20 + 90 + 10 + 180 + 85),其設定的左邊距為 100px,100 < 285。則【C】不滿足觸發此 Bug 的條件。【C】實際的左邊距仍然為 100px。

上面討論的都是元素浮動之前為塊級元素,下面觀察一下 'display' 特性分別為 'inline' 及 'block' 的 SPAN 元素浮動後的情況:

<div style="width:100px; height:20px; background:#ccc;">100 x 20</div>
<span style="float:left; margin-left:100px; width:100px; height:20px; background:gray;">inline FLOAT</span>
<br /><br />
<div style="width:100px; height:20px; background:#ccc;">100 x 20</div>
<span style="float:left; margin-left:100px; width:100px; height:20px; background:gray; display:block">block FLOAT</span>

這段程式碼在不同瀏覽器中執行效果為:

IE5.0
IE5.5
IE6
IE5-6 double margins
IE7
IE8
Firefox
Chrome
Safari
Opera
non IE5-6 double margins

從上面一組截圖中很明顯的看出,雙邊距 Bug 會作用於 'display' 特性為 'block' 的元素,對於 'inline' 的元素不會觸發此 Bug。

解決方案

  • 儘量避免同時使用 'margin-left' 與 float:left,及 'margin-right' 與 float:right;
  • 由於這個 Bug 對於 'display' 特性為 'inline' 的元素不會觸發,所以可以通過設定 display:inline 消除此 Bug,由於此 Bug 僅在元素浮動時發生,而浮動將使該元素 'display' 特性計算為 'block' 或者 'table'(見 CSS2.1 規範第 9.7 Relationships between 'display', 'position', and 'float' 節),因此可以通過設定 display:inline 消除雙邊距 Bug。


相關文章