由position屬性引伸的關於css的進階討論(包含塊、BFC、margin collapse)

寫這篇文章的原由是源於這篇文章:談談面試與面試題 中關於position的討論,文中一開始就說的這句話:css

面試的時候問個css的position屬性能刷掉一半的人這是啥狀況……html

其實這問題我原本打算的是能夠順着一路扯到normal flow、containing block、bfc、margin collapse,base line,writing mode,bidi,這樣一路問下去的,奈何第一個問題(親我真的只問了position有哪些取值和行爲啊)就悲劇了……git

說到position,那麼稍微對css有所瞭解的必然能立刻說出它的四個屬性值:static 、relative、absolute、fixed。可是更深一步去討論,牽扯出諸如上文提到的normal flowcontaining blockbfcmargin collapsebase linewriting modebidi,又有多少人能很好的回答完整呢,因此我想在此作一個本身的總結概括。github

 

1.normal flow         面試

normal flow(正常流):正常流是默認的定位方式。任何沒有具體指定{position:absolute}或者{position:fixed}屬性以及沒有被浮動的元素都將默認得到此屬性。瀏覽器

在這種方式裏,塊級元素在它們的包含塊裏一個一個垂直延伸,行內元素在它們的包含塊裏從左至右的水平排布。app

值得注意的是,在正常流裏垂直邊距(vertical margin)是重疊的。也就是說,上下兩個塊級盒之間的邊距由它們之中邊距較大的元素決定,而不是他們的和!ide

 1 <style>
 2 div  3 {
 4  width: 100px;
 5  height: 100px;
 6  border: 1px solid #00A4CC;
 7  background-color: #3e8f3e;
 8 }
 9 .div1 10 {
11  margin:20px 0;
12 }
13 .div2 14 {
15  margin:40px 0;
16 }
17 </style>
18 </head>
19 
20 <body>
21 <div class="div1"></div>
22 <div class="div2"></div>
23 </body>

效果顯示以下:佈局

div1和div2的垂直距離由大的margin決定,也就是div2的40px而不是兩者之和60px。性能

其次,行內元素是會被折斷的,當寬度受到限制的時候,它會自動移動到下一行。這可能會產生一些難看的效果若是行內塊有邊框的話。看下面的效果:

 1 <head>
 2 <style>
 3 div  4 {
 5  width: 200px;
 6  height: 100px;
 7  border: 1px solid #00A4CC;
 8 }
 9 
10 span 11 {
12  border: 2px solid #00ff00;
13 }
14 </style>
15 </head>
16 
17 <body>
18 <div>
19     <span> 我會掉到下一行我會掉到下一行我會掉到下一行</span>
20 </div>
21 </body>

效果顯示以下:

2.containing block        

containing block(包含塊):是視覺格式化模型的一個重要概念,它與框模型相似,也能夠理解爲一個矩形,而這個矩形的做用是爲它裏面包含的元素提供一個參考,元素的尺寸和位置每每是由該元素所在的包含塊決定的。也就是說一個元素盒子的位置和大小有時是經過相對於一個特定的長方形來計算的,這個長方形就被稱之爲元素的 containing block。

一個元素的containing block按照如下方式定義:

  1. 用戶代理(好比瀏覽器)選擇根元素做爲 containing block(稱之爲初始 containing block)。

  2. 對於其它元素,除非元素使用的是絕對位置,containing block 由最近的塊級祖先元素盒子的內容邊界組成。

  3. 若是元素有屬性 'position:fixed',containing block 由視口創建。

  4. 若是元素有屬性 'position:absolute',containing block 由最近的 position 不是 static 的祖先創建,按下面的步驟:

    • 若是祖先是塊級元素,containing block 由祖先的 padding edge 造成。

    • 若是祖先是內聯元素,containing block 取決於祖先的 direction 屬性。

      • 若是 direction 是 ltr(左到右),祖先產生的第一個盒子的上、左內容邊界是 containing block 的上方和左方,祖先的最後一個盒子的下、右內容邊界是 containing block 的下方和右方。

      • 若是 direction 是 rtl(右到左),祖先產生的第一個盒子的上、右內容邊界是 containing block 的上方和右方,祖先的最後一個盒子的下、左內容邊界是 containing block 的下方和左方。

  5.若是沒有祖先,根元素盒子的內容邊界肯定爲 containing block。

名詞解釋:

視口:經過解析文檔,連續媒體(好比屏幕就是連續媒體,而打印機則是基於頁的媒體)給用戶產生一個視口(一個窗口或其它在屏幕上顯示的區域)。

根元素:源文件中,每個元素都有一個父元素,只有一個例外,它就是根元素。

padding edge:請參見下圖:

舉個板栗:

 1 <html>
 2    <head>
 3       <title>Illustration of containing blocks</title>
 4    </head>
 5    <body id="body">
 6       <div id="div1">
 7         <p id="p1">This is text in the first paragraph...</P>
 8           <p id="p2">This is text  9             <em id="em1"> in the 10               <strong id="strong1">second 11               </strong> paragraph. 12             </em>
13           </p>
14       </div>
15    </body>
16 </html>    

那麼,在沒有指定任何position的狀況下,上方代碼的containng block肯定方式爲:

若是咱們設置div1的position爲:

1 #div1 { position: absolute;}

此時,div1 的 containing block 就再也不是 body,它變成了初始 containing block(由於這裏尚未具備 position 的祖先盒子)。

 

3.BFC        

BFC(Block Formatting Context 塊格式化上下文):是W3C CSS 2.1 規範中的一個概念,在CSS3中被修改成flow root。格式化則代表了在這個環境中,元素處於此環境中應當被初始化,即元素在此環境中應當如何佈局等。元素若是建立了BF麼BFC決定了如何對其內容進行定位,以及它與其餘元素的關係和相互做用。

通俗理解:首先BFC是一個名詞,是一個獨立的佈局環境,咱們能夠理解爲一個箱子(其實是看不見摸不着的),箱子裏面物品的擺放是不受外界的影響的。轉換爲BFC的理解則是:BFC中的元素的佈局是不受外界的影響(咱們每每利用這個特性來消除浮動元素對其非浮動的兄弟元素和其子元素帶來的影響。)而且在一個BFC中,塊盒與行盒(行盒由一行中全部的內聯元素所組成)都會垂直的沿着其父元素的邊框排列。

建立了BFC的元素會按照以下的方式對其子元素進行排列:

  1. 在BFC中,盒子從頂端開始垂直地一個接一個地排列,兩個盒子之間的垂直的間隙是由他們的margin 值所決定的。在一個BFC中,兩個相鄰的塊級盒子的垂直外邊距會產生摺疊。便是在BFC中相鄰的塊級元素的垂直邊距會摺疊(collapse)。
  2. 在BFC中,每個盒子的左外邊緣(margin-left)會觸碰到容器的左邊緣(border-left)(對於從右到左的格式來講,則觸碰到右邊緣), 即便存在浮動也是如此(儘管一個子元素的內容區域會因爲浮動而壓縮),除非這個子元素也建立了一個新的BFC,如它自身也是一個浮動。

名詞解釋:

邊距摺疊:在CSS當中,相鄰的兩個盒子(多是兄弟關係也多是祖先關係)的外邊距能夠結合成一個單獨的外邊距。這種合併外邊距的方式被稱爲摺疊,而且於是所結合成的外邊距稱爲摺疊外邊距。

摺疊的結果: 

  1. 兩個相鄰的外邊距都是正數時,摺疊結果是它們二者之間較大的值。
  2. 兩個相鄰的外邊距都是負數時,摺疊結果是二者絕對值的較大值。
  3. 兩個外邊距一正一負時,摺疊結果是二者的相加的和。

產生摺疊的必備條件:margin必須是鄰接的,且須要知足以下條件:

  1. 必須是處於常規文檔流(非float和絕對定位)的塊級盒子,而且處於同一個BFC當中。
  2. 沒有線盒,沒有空隙(clearance),沒有padding和border將他們分隔開
  3. 都屬於垂直方向上相鄰的外邊距,能夠是下面任意一種狀況
    • 元素的margin-top與其第一個常規文檔流的子元素的margin-top
    • 元素的margin-bottom與其下一個常規文檔流的兄弟元素的margin-top
    • height爲auto的元素的margin-bottom與其最後一個常規文檔流的子元素的margin-bottom
    • 高度爲0而且最小高度也爲0,不包含常規文檔流的子元素,而且自身沒有創建新的BFC的元素的margin-top和margin-bottom

那麼,什麼狀況下會建立BFC:  

  1. 浮動元素(float: left | right)
  2. 絕對定位元素(position: absolute | fixed)
  3. 行內塊元素(display: inline-block)
  4. 表格的單元格(display: table-cells,TD、TH)
  5. 表格的標題(display: table-captions,CAPTION)
  6. 'overflow' 特性不爲 visible 的元素
  7. 表格元素建立的 "匿名框"

值得注意的是,"display:table" 自己並不產生 "block formatting contexts"。可是,它能夠產生匿名框, 其中包含 "display:table-cell" 的框會產生塊格式化上下文。

總之,對於 "display:table" 的元素,產生塊格式化上下文的是匿名框而不是 "display:table"。

最後,是這些元素建立了塊格式化上下文,它們自己不是塊格式化上下文。

BFC的運用:消除浮動與多欄佈局。

1.自適應兩欄佈局

 1 <style>
 2  body {
 3  position: relative;
 4     }
 5 
 6  .aside {
 7  width: 100px;
 8  height: 150px;
 9  float: left;
10  background: #f66;
11     }
12 
13  .main {
14  height: 200px;
15  background: #fcc;
16     }
17 </style>
18 <body>
19     <div class="aside"></div>
20     <div class="main"></div>
21 </body>

效果以下:

根據BFC的規則:

每個盒子的左外邊緣(margin-left)會觸碰到容器的左邊緣(border-left)(對於從右到左的格式來講,則觸碰到右邊緣)

因此,雖然存在浮動的元素aslide,但main的左邊依然會與包含塊的左邊相接觸。

所以,咱們能夠根據:

BFC的區域不會與float box重疊。

來經過觸發main生成BFC,實現自適應兩欄佈局。

1 .main { 2  overflow: hidden; 3 }

當觸發main生成BFC後,這個新的BFC不會與浮動的aside重疊。所以會根據包含塊的寬度,和aside的寬度,自動變窄。效果以下:

2.清除內部浮動

 1 <head>
 2     <title>Clear float</title>
 3     <style>
 4  .container{
 5  margin: 30px auto;
 6  width:600px;
 7  height: 300px;
 8         }
 9  .wrapper{
10  border:solid 3px #a33;
11         }
12  .main{
13  width: 100px;
14  height: 100px;
15  background-color: #060;
16  margin: 10px;
17  float: left;
18         }
19     </style>
20 </head>
21 <body>
22     <div class="container">
23         <div class="wrapper">
24             <div class="main"></div>
25             <div class="main"></div>
26             <div class="main"></div>
27         </div>
28     </div>
29 </body>

但願的結果是:

但結果是:

父容器並無把浮動的子元素包圍起來,俗稱塌陷,爲了消除這種現象,除了用傳統的僞類方法。根據

計算BFC的高度時,浮動元素也參與計算

還可使父容器造成BFC,來清除浮動,簡單修改一下代碼:

<div class="wrapper" style="float:left;"> //添加一個float屬性,造成BFC <div class="main">1</div>
    <div class="main">2</div>
    <div class="main">3</div>
</div>

能夠獲得以下效果:

總結來講,BFC就是頁面上的一個隔離的獨立容器,容器裏面的子元素不會影響到外面的元素。反之也如此。

由於BFC內部的元素和外部的元素絕對不會互相影響,所以,當BFC外部存在浮動時,它不該該影響BFC內部Box的佈局,BFC會經過變窄,而不與浮動有重疊。一樣的,當BFC內部有浮動時,爲了避免影響外部元素的佈局,BFC計算高度時會包括浮動的高度。避免margin重疊也是這樣的一個道理。

相關文章
相關標籤/搜索