Block Elements and inline elements(塊元素和行內元素)


在進入正題之前,我們先來簡單總結一下傳統的block元素和inline元素。

  • HTML中的block元素顯示在頁面上時總會另起一行,並佔滿它的父元素的整個寬度;block元素的寬高可以通過width和height來設定;
  • inline元素正好相反,它們不會另起一行,只會佔展示內容所需的寬度;width和height對inline元素不起作用;
  • 另外,還有一種處於block元素和inline元素之間的inline-block元素,這種元素和inline元素一樣,不會新起一行,但是可以通過width和height來設定它的寬高;

HTML元素都有其預設顯示方式,如<p>元素預設為block元素,但我們還可以通過設定 display: block; display: inline; display: inline-block; 來改變元素的顯示方式;

Flex-box模型


Flex-box模型讓我們可以使用 display: flex; 把一個元素變成flex容器,不管這個元素原來是block元素還是inline元素,當它變成flex容器之後,它的行為會類似block元素,會佔滿父元素的寬度,也可以通過width和height來設定它的寬高;

讓我們通過一個例子來說明,如下面的HTML程式碼所示,我們用一個<span>元素把三個<div>元素包裹起來,一般來說我們不會這麼做,這裡只是為了說明不管是block元素還是inline元素,只要對它設定了 display: flex; 它就會變成一個類似block元素的容器。

<span class="container">
<div class="item">item1</div>
<div class="item">item2</div>
<div class="item">item3</div>
</span>

預設情況下它是這樣子顯示的(為了方便展示我給<span>和<div>分別添加了2px的邊框):

當我們給<span>元素加上 display: flex; 之後:

<span>元素變成了一個flex容器,行為類似block元素,而且寬度和高度也可以設定;但奇怪的是,在<span>元素中的三個<div>元素按照規則應該是從上往下疊放,並且每一個都要佔滿父元素<span>寬度的,但它們反而是並排顯示的。這是因為當一個元素變成flex容器之後,它的所有直接子元素都會變成flex item(類似inline-block),預設只會佔內容展示所需要的寬度,而且不會另起一行;

讓我們給<span>元素設定一下寬高 width: 300px; height: 150px; :

有沒有發現現在三個flex item的高度跟父元素的高度是一樣,這是因為 align-items 的預設值是 stretch;

當一個元素變成flex容器之後,它的所有直接子元素(flex item)就擁有了三個神奇的屬性 flex-grow, flex-shrink, flex-basis,這三個屬性是用來設定一個 flex item 要佔多少寬度的;預設情況下他們的值分別是:

  • flex-grow: 0;
  • flex-shrink: 1;
  • flex-basis: auto;

flex-basis


flex-basis 相當於 flex item 的 width, 當為一個flex item設定了 flex-basis: auto; 時,如果同時為這個flex item 設定了width屬性,那麼這個flex item的寬度就等於width屬性的值,如果沒有設定width屬性,那麼這個flex item只會佔展示內容所需的寬度;讓我們給第一個flex item設定 width: 100px;

現在第一個flex item的寬度變成了100px,但這不是一個好的做法,最好還是為一個flex item直接設定flex-basis而不是width屬性;

如果我們給flex-basis指定一個除了auto以外的值,那麼flex item的width屬性的值就會被忽略掉;當我們同時給例子中的第一個flex item設定 width: 100px; flex-baisi: 50px;

現在第一個flex item的寬度變成了50px,它的width屬性被忽略了;

flex-basis還接受其他的CSS單位,例如我們可以給每個flex item設定 flex-basis: 30%;

現在每個flex item的寬度都是父元素寬度的30%,那具體是多少呢?

之前我們設定了父元素的width等於300px,而且因為我們沒有設定 box-sizing: border-box; ,所以這個flex容器的內寬度(不包括padding和border)就是300px,那每個flex item的內寬度就是 300px * 30% = 90px ,但因為每個flex item都有一個2px寬的邊框,所以每個flex item的總寬度(內寬度+邊框)是 90px + 2px * 2 = 94px ;

但如果我們給flex容器設定了 box-sizing: border-box; width: 300px; ,現在這個flex容器的總寬度是300px,但它也有一個2px寬的邊框,所以它的內寬度變成是 300px - 2px * 2 = 296px ,這時每個flex item的內寬度就變成了 296px * 30% = 88.8px ,總寬度則還要加上兩邊的邊框 88.8px + 2px * 2 = 92.8px ;

現在我們去掉flex容器的 box-sizing: border-box; 設定,它的內寬度又變回了300px;我們再給每個flex item設定 box-sizing: border-box; padding: 10px; ,現在每個flex item的總寬度(內寬度+padding+border)變成了 300px * 30% = 90px ;我們再給第一個flex item兩邊加上margin, margin: 0 10px; 可以看到這並不會影響它的寬度;

 flex-shrink


現在把第一個flex item的 margin: 0 10px; 刪掉:

flex容器的width還是300px,每個flex item的總寬度是 300px * 30% = 90px ,三個flex item加起來一共佔了 90px * 3 = 270px ,小於flex容器的寬度;如果我們給每個flex item設定 flex-basis: 150px; 總寬度會超過flex容器的寬度,那會不會發生內容溢位的情況?

其實並不會,因為我們上面提到過,每個flex item都會預設設定 flex-shrink: 1; ,這個屬性的作用就是當flex item寬度的總和大於flex容器的寬度時,適當地縮小flex item;

假如我們讓每個flex item的寬度等於200px,三個加起來就是600px,然而flex容器只有300px寬,那麼多出來300px就要被砍掉啦,因為每個flex item的flex-shrink都預設為1,所以它們會相應地縮小: 300px / 3 = 100px ,那麼每個flex item縮小後的寬度就變成了 200px - 100px = 100px ;

flex-shrink還可以接收大於1的值,現在我們把第一個flex item設定為 flex-shrink: 2;

多出來的那300px會被分成 2 + 1 + 1 = 4 份,每一份是 300px / 4 = 75px ,第一個flex item會相應縮小兩份變成 200px - 75px * 2 = 50px ,其他兩個會分別縮小一份變成 200px - 75px = 125px ;

但flex-shrink不僅僅會根據多出來的那部分寬度縮小flex item的寬度,它還會根據flex item的flex-baisi的值;上面的例子中每個flex item的寬度都一樣,現在我們把第一個flex item的設定為 flex-basis: 100px; flex-shrink: 1; , 並對第二和第三個flex item設定 flex-basis: 300px; flex-shrink: 2;  ,現在三個flex item的總寬度是 100px + 300px * 2 = 700px ,而flex容器的寬度是300px,所以多出來的寬度是400px;

首先找出最小的寬度,在這個例子裡是100px,然後用這個寬度去除每個flex item的寬度,得到:

  • 100px / 100px = 1;
  • 300px / 100px = 3;
  • 300px / 100px = 3;

然後用得到的比例分別乘以它們的flex-shrink的值:

  • 1 * 1 = 1;
  • 3 * 2 = 6;
  • 3 * 2 = 6;

得到的值加起來就是 1 + 6 + 6 = 13;

接下來就是計算每一個flex item應該縮小多少了,在這個例子中,多出來的寬度是400px,所以每個flex item分別約縮小:

  • 400px * 1/13 = 30px
  • 400px * 6/13 = 185px
  • 400px * 6/13 = 185px

再用它們的flex-basis的值減去它們應該縮小的寬度就可以得到它們的實際寬度:

  • 100px - 30px = 70px
  • 300px - 185px = 115px
  • 300px - 185px = 115px

把這些加起來 70px + 115px + 115px = 300px 正好是flex容器的寬度;

flex-grow


現在我們給每個flex item設定 flex-basis: 50px; box-sizing: boreder-box; ,flex容器的寬度依然是300px:

可以看到flex容器還有剩餘的空間,但是flex item不會把它填滿,因為它們的flex-grow預設值為0;如果我們把flex-grow設定為大於0的值,那麼flex item就會相應地擴大以填滿flex容器;現在我們給每個flex item設定 flex-grow: 1; 它們就會把flex容器剩餘的空間填滿:

現在每個flex item的寬度都是100px,我們來看一下這是怎麼算出來的:

  • 首先flex容器的width是300px,三個flex item加起來的寬度是 50px * 3 = 150px ,所以剩餘的空間是 300px - 150px = 150px ;
  • 我們有三個flex item,而且每一個的flex-grow的值都是1,所以剩餘空間會被分成 1 + 1 + 1 = 3 份, 每一份是 150px / 3 = 50px ;
  • 每一個flex item會增大50px寬,最後變成 50px + 50px = 100px ;

現在我們給第一個flex item設定 flex-grow: 3; 其他兩個還是 flex-grow: 1; ,這個意思並不是說第一個flex item會是其他flex item的3倍:

現在第一個flex item的寬度變成了140px,其他兩個則分別是80px,我們來看看是怎麼算的吧:

首先flex容器的剩餘空間還是150px,但這次不是被分成3份了,而是會分成 3 + 1 + 1 = 5 份(每個flex item的flex-grow的值相加),每一份是30px;

所以三個flex item分別會增加:

  • 3 * 30px = 90px
  • 1 * 30px = 30px
  • 1 * 30px = 30px

最終的寬度分別是:

  • 50px + 90px = 140px
  • 50px + 30px = 80px
  • 50px + 30px = 80px