關於Block Formatting Context--BFC和IE的hasLayout


轉文請標明 --- 出處:穆乙 http://www.cnblogs.com/pigtail/

一、BFC是什么?

BFC(Block Formatting Context)直譯為“塊級格式化范圍”。

是 W3C CSS 2.1 規范中的一個概念,它決定了元素如何對其內容進行定位,以及與其他元素的關系和相互作用。當涉及到可視化布局的時候,Block Formatting Context提供了一個環境,HTML元素在這個環境中按照一定規則進行布局。一個環境中的元素不會影響到其它環境中的布局。比如浮動元素會形成BFC,浮動元素內部子元素的主要受該浮動元素影響,兩個浮動元素之間是互不影響的。這里有點類似一個BFC就是一個獨立的行政單位的意思。也可以說BFC就是一個作用范圍。可以把它理解成是一個獨立的容器,並且這個容器的里box的布局,與這個容器外的毫不相干。

另一個通俗點的解釋是:在普通流中的 Box(框) 屬於一種 formatting context(格式化上下文) ,類型可以是 block ,或者是 inline ,但不能同時屬於這兩者。並且, Block boxes(塊框) 在 block formatting context(塊格式化上下文) 里格式化, Inline boxes(塊內框) 則在 inline formatting context(行內格式化上下文) 里格式化。任何被渲染的元素都屬於一個 box ,並且不是 block ,就是 inline 。即使是未被任何元素包裹的文本,根據不同的情況,也會屬於匿名的 block boxes 或者 inline boxes。所以上面的描述,即是把所有的元素划分到對應的 formatting context 里。

其一般表現規則,我整理了以下這幾個情況:

1、在創建了 Block Formatting Context 的元素中,其子元素按文檔流一個接一個地放置。垂直方向上他們的起點是一個包含塊的頂部,兩個相鄰的元素之間的垂直距離取決於 ‘margin’ 特性。

    根據 CSS 2.1 8.3.1 Collapsing margins 第一條,兩個相鄰的普通流中的塊框在垂直位置的空白邊會發生折疊現象。也就是處於同一個BFC中的兩個垂直窗口的margin會重疊。

    根據 CSS 2.1 8.3.1 Collapsing margins 第三條,生成 block formatting context 的元素不會和在流中的子元素發生空白邊折疊。所以解決這種問題的辦法是要為兩個容器添加具有BFC的包裹容器。

2、在 Block Formatting Context 中,每一個元素左外邊與包含塊的左邊相接觸(對於從右到左的格式化,右外邊接觸右邊), 即使存在浮動也是如此(盡管一個元素的內容區域會由於浮動而壓縮),除非這個元素也創建了一個新的 Block Formatting Context 。

3、Block Formatting Context就是頁面上的一個隔離的獨立容器,容器里面的子元素不會在布局上影響到外面的元素,反之也是如此。

4、根據 CSS 2.1 9.5 Floats 中的描述,創建了 Block Formatting Context 的元素不能與浮動元素重疊。

    表格的 border-box、塊級的替換元素、或是在普通流中創建了新的 block formatting context(如元素的 'overflow' 特性不為 'visible' 時)的元素不可以與位於相同的 block formatting context 中的浮動元素相重疊。

5 、當容器有足夠的剩余空間容納 BFC 的寬度時,所有瀏覽器都會將 BFC 放置在浮動元素所在行的剩余空間內。 

6、 在 IE6 IE7 IE8 Chrome Opera 中,當 BFC 的寬度介於 "容器剩余寬度" 與 "容器寬度" 之間時,BFC 會顯示在浮動元素的下一行;在 Safari 中,BFC 則仍然保持顯示在浮動元素所在行,並且 BFC 溢出容器;在 Firefox 中,當容器本身也創建了 BFC 或者容器的 'padding-top'、'border-top-width' 這些特性不都為 0 時表現與 IE8(S)、Chrome 類似,否則表現與 Safari 類似。 

經驗證,最新版本的瀏覽中只有firefox會在同一行顯示,其它瀏覽器均換行。

7、 在 IE6 IE7 IE8 Opera 中,當 BFC 的寬度大於 "容器寬度" 時,BFC 會顯示在浮動元素的下一行;在 Chrome Safari 中,BFC 則仍然保持顯示在浮動元素所在行,並且 BFC 溢出容器;在 Firefox 中,當容器本身也創建了 BFC 或者容器的 'padding- top'、'border-top-width' 這些特性不都為 0 時表現與 IE8(S) 類似,否則表現與 Chrome 類似。

經驗證,最新版本的瀏覽中只有firefox會在同一行顯示,其它瀏覽器均換行。

8、根據CSS2.1 規范第10.6.7部分的高度計算規則,在計算生成了 block formatting context 的元素的高度時,其浮動子元素應該參與計算。

如果還有其它情況,請各位回得中補充,我會及時更新!

下面先看一個比較典型的例子:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>無標題文檔</title>
<style>
* { padding:0; margin:0; }
#red, #yellow, #orange, #green { width:100px; height:100px; float:left; }
#red { background-color:red; }
#yellow { background-color:yellow; }
#orange { background-color:orange; }
#green { background-color:green; }
</style>
</head>

<body>
<div id="c1">
    <div id="red">
    </div>
    <div id="yellow">
    </div>
</div>
<div id="c2">
    <div id="orange">
    </div>
    <div id="green">
    </div>
</div>
<p>Here is the text!</p>
</body>
</html>

效果如下:

該段代碼本意要形成兩行兩列的布局,但是由於#red,#yellow,#orange,#green四個div在同一個布局環境BFC中,因此雖然它們位於兩個不同的div(#c1和#c2)中,但仍然不會換行,而是一行四列的排列。

若要使之形成兩行兩列的布局,就要創建兩個不同的布局環境,也可以說要創建兩個BFC。那到底怎么創建BFC呢?

二、如何產生BFC:當一個HTML元素滿足下面條件的任何一點,都可以產生Block Formatting Context:

  • float的值不為none。
  • overflow的值不為visible。
  • display的值為table-cell, table-caption, inline-block中的任何一個。
  • position的值不為relative和static。

如果還其它方式,請在回復中給出,我會及時更新!!

上面的例子,我再加兩行代碼,創建兩個BFC:

#c1{overflow:hidden;}
#c2{overflow:hidden;}

效果如下:

上面創建了兩個布局環境BFC。內部子元素的左浮動不會影響到外部元素。所以#c1和#c2沒有受浮動的影響,仍然各自占據一行!

三、BFC能用來做什么?

 a、不和浮動元素重疊

如果一個浮動元素后面跟着一個非浮動的元素,那么就會產生一個覆蓋的現象,很多自適應的兩欄布局就是這么做的。

看下面一個例子

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>無標題文檔</title>
<style>
html,body {height:100%; }
* { padding:0; margin:0; color:#fff; text-decoration:none; list-style:none; font-family:"微軟雅黑" }
.aside{background:#f00;width:170px;float:left;height:300px;}
.main{background:#090;height:100%;}
</style>
</head>

<body>
<div class="aside">
</div>
<div class="main">
</div>
</body>
</html>

效果圖如下:

很明顯,.aside和.mian重疊了。試分析一下,由於兩個box都處在同一個BFC中,都是以BFC邊界為起點,如果兩個box本身都具備BFC的話,會按順序一個一個排列布局,現在.main並不具備BFC,按照規則2,內部元素都會從左邊界開始,除非它本身具備BFC,按上面規則4擁有BFC的元素是不可以跟浮動元素重疊的,所以只要為.mian再創建一個BFC,就可以解決這個重疊的問題。上面已經說過創建BFC的方法,可以根據具體情況選用不同的方法,這里我選用的是加overflow:hidden。

由於ie的原因需要再加一個解發haslayout的zoom:1,有關haslayout后面會講到。

b、清除元素內部浮動

只要把父元素設為BFC就可以清理子元素的浮動了,最常見的用法就是在父元素上設置overflow: hidden樣式,對於IE6加上zoom:1就可以了(IE Haslayout)。

 看下面例子:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>無標題文檔</title>
<style>
html,body {height:100%; }
* { padding:10px; margin:0; color:#000; text-decoration:none; list-style:none; font-family:"微軟雅黑" }
.outer{width:300px;border:1px solid #666;padding:10px;}
.innerLeft{height:100px;width:100px;float:left;background:#f00;}
.innerRight{height:100px;width:100px;float:right;background:#090;}
</style>
</head>

<body>
<div class="outer">
  <div class="innerLeft"></div>
  <div class="innerRight"></div>
</div>
</div>
</body>
</html>

效果圖如下:

根據 CSS2.1 規范第 10.6.3 部分的高度計算規則,在進行普通流中的塊級非替換元素的高度計算時,浮動子元素不參與計算。

同時 CSS2.1 規范第10.6.7部分的高度計算規則,在計算生成了 block formatting context 的元素的高度時,其浮動子元素應該參與計算。

所以,觸發外部容器BFC,高度將重新計算。比如給outer加上屬性overflow:hidden觸發其BFC。

c、解決上下相鄰兩個元素重疊

 看下面例子:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>無標題文檔</title>
<style>
html,body {height:100%; }
* { padding:0; margin:0; color:#fff; text-decoration:none; list-style:none; font-family:"微軟雅黑" }
.rowone{background:#f00;height:100px;margin-bottom:20px;overflow:hidden;}
.rowtow{background:#090;height:100px;margin-top:20px;position:relative}
</style>
</head>

<body>
<div class="rowone">
</div>
<div class="rowtow">
</div>
</body>
</html>

效果如下:

根據 CSS 2.1 8.3.1 Collapsing margins 第一條,兩個相鄰的普通流中的塊框在垂直位置的空白邊會發生折疊現象。也就是處於同一個BFC中的兩個垂直窗口的margin會重疊。

根據 CSS 2.1 8.3.1 Collapsing margins 第三條,生成 block formatting context 的元素不會和在流中的子元素發生空白邊折疊。所以解決這種問題的辦法是要為兩個容器添加具有BFC的包裹容器。

所以解這個問題的辦法就是,把兩個容器分別放在兩個據有BFC的包裹容器中,IE里就是觸發layout的兩個包裹容器中!

 

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>無標題文檔</title>
<style>
html, body { height:100%; }
* { padding:0; margin:0; color:#fff; text-decoration:none; list-style:none; font-family:"微軟雅黑" }
.mg {overflow:hidden; }
.rowone { background:#f00; height:100px; margin-bottom:20px; }
.rowtow { background:#090; height:100px; margin-top:20px; }
</style>
</head>

<body>
<div class="mg">
    <div class="rowone">
    </div>
</div>
<div class="mg">
    <div class="rowtow">
    </div>
</div>
</body>
</html>

效果如下:

 

四、什么是IE的haslayout

上面的例子中我們用到了IE的zoom:1;實際上是觸發了IE的layout。Layout 是 IE 瀏覽器渲染引擎的一個內部組成部分。在 IE 瀏覽器中,一個元素要么自己對自身的內容進行組織和計算大小, 要么依賴於包含塊來計算尺寸和組織內容。為了協調這兩種方式的矛盾,渲染引擎采用了 ‘hasLayout’ 屬性,屬性值可以為 true 或 false。 當一個元素的 ‘hasLayout’ 屬性值為 true 時,我們說這個元素有一個布局(layout),或擁有布局。可以通過 hasLayout 屬性來判斷一個元素是否擁有 layout ,

如 object.currentStyle.hasLayout 。

hasLayout 與 BFC 有很多相似之處,但 hasLayout 的概念會更容易理解。在 Internet Explorer 中,元素使用“布局”概念來控制尺寸和定位,分為擁有布局和沒有布局兩種情況,擁有布局的元素由它控制本身及其子元素的尺寸和定位,而沒有布局的元素則通過父元素(最近的擁有布局的祖先元素)來控制尺寸和定位,而一個元素是否擁有布局則由 hasLayout 屬性告知瀏覽器,它是個布爾型變量,true 代表元素擁有布局,false 代表元素沒有布局。簡而言之,hasLayout 只是一個 IE 下專有的屬性,hasLayout 為 true 的元素瀏覽器會賦予它一系列的效果。

特別注意的是,hasLayout 在 IE 8 及之后的 IE 版本中已經被拋棄,所以在實際開發中只需針對 IE 8 以下的瀏覽器為某些元素觸發 hasLayout。

 

五、怎樣觸發layout

一個元素觸發 hasLayout 會影響一個元素的尺寸和定位,這樣會消耗更多的系統資源,因此 IE 設計者默認只為一部分的元素觸發 hasLayout (即默認有部分元素會觸發 hasLayout ,這與 BFC 基本完全由開發者通過特定 CSS 觸發並不一樣),這部分元素如下:

<html>, <body>
<table>, <tr>, <th>, <td>
<img>
<hr>
<input>, <button>, <select>, <textarea>, <fieldset>, <legend>
<iframe>, <embed>, <object>, <applet>
<marquee>

除了 IE 默認會觸發 hasLayout 的元素外,Web 開發者還可以使用特定的 CSS 觸發元素的 hasLayout 。

通過為元素設置以下任一 CSS ,可以觸發 hasLayout (即把元素的 hasLayout 屬性設置為 true)。

display: inline-block
height: (除 auto 外任何值)
width: (除 auto 外任何值)
float: (left 或 right)
position: absolute
writing-mode: tb-rl
zoom: (除 normal 外任意值)
min-height: (任意值)
min-width: (任意值)
max-height: (除 none 外任意值)
max-width: (除 none 外任意值)
overflow: (除 visible 外任意值,僅用於塊級元素)
overflow-x: (除 visible 外任意值,僅用於塊級元素)
overflow-y: (除 visible 外任意值,僅用於塊級元素)
position: fixed

對於內聯元素(可以是默認被瀏覽器認為是內聯元素的 span 元素,也可以是設置了 display: inline 的元素),width 和 height 只在 IE5.x 下和 IE6 或更新版本的 quirks 模式下能觸發元素的 hasLayout ,但是對於 IE6,如果瀏覽器運行於標准兼容模式下,內聯元素會忽略 width 或 height 屬性,所以設置 width 或 height 不能在此種情況下令該元素觸發 hasLayout 。但 zoom 除了在 IE 5.0 中外,總是能觸發 hasLayout 。zoom 用於設置或檢索元素的縮放比例,為元素設置 zoom: 1 既可以觸發元素的 hasLayout 同時不會對元素造成多余的影響。因此綜合考慮瀏覽器之間的兼容和對元素的影響, 建議使用 zoom: 1 來觸發元素的 hasLayout 。

六、能解決的問題

hasLayout表現出來的特性跟BFC很相似,所以可以認為是IE中的BFC。上面的規則幾乎都遵循,所以上面的問題在IE里都可以通過觸發hasLayout來解決。

雖然 hasLayout 也會像 BFC 那樣影響着元素的尺寸和定位,但它卻又不是一套完整的標准,並且由於它默認只為某些元素觸發,這導致了 IE 下很多前端開發的 bugs ,觸發 hasLayout 更大的意義在於解決一些 IE 下的 bugs ,而不是利用它的一些“副作用”來達到某些效果。另外由於觸發 hasLayout 的元素會出現一些跟觸發 BFC 的元素相似的效果,因此為了統一元素在 IE 與支持 BFC 的瀏覽器下的表現,Kayo 建議為觸發了 BFC 的元素同時觸發 hasLayout ,當然還需要考慮實際的情況,也有可能只需觸發其中一個就可以達到表現統一,下面會舉例介紹。

這里首先列出觸發 hasLayout 元素的一些效果:

a、阻止外邊距折疊

 如上面例子:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>無標題文檔</title>
<style>
html, body { height:100%; }
* { padding:0; margin:0; color:#fff; text-decoration:none; list-style:none; font-family:"微軟雅黑" }
.mg {zoom:1}
.rowone { background:#f00; height:100px; margin-bottom:20px; }
.rowtow { background:#090; height:100px; margin-top:20px; }
</style>
</head>

<body>
<div class="mg">
    <div class="rowone">
    </div>
</div>
<div class="mg">
    <div class="rowtow">
    </div>
</div>
</body>
</html>

需要觸發.mg的layout才能解決margin重疊問題

運行效果如下:

 

上面有關BFC所舉的例子,在IE6\7中觸發layout都可以解決,可以自己動手試一下,這里就不重復舉例了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM