常見 CSS 布局方式

前言

溫馨提示:本文較長,圖片較多,本來是想寫一篇 CSS 布局方式的,但是奈何 CSS 布局方式種類太多并且實現方法太多,所以本文主要是介紹 flex 布局和 grid 布局,以及 CSS 常見的居中方式和兩種經典的布局方式“圣杯布局”和“雙飛翼布局”。想到哪寫到哪,請各位見諒。

本文思維導圖,歡迎補充

傳統盒模型布局方式

我們的傳統布局方式就是通過盒模型,使用 display 屬性(文檔流布局) + position 屬性(定位布局) + float屬性(浮動布局)。這個大家都比較熟悉,沒有掌握的同學再去惡補一下基礎

文檔流布局

這是最基本的布局方式,就是按照文檔的順序一個一個顯示出來,塊元素獨占一行,行內元素共享一行,這個相信大家都比較熟悉了,就不再贅述了

浮動布局

浮動方式布局就是使用 float 屬性,使元素脫離文檔流,浮動起來。這個大家也比較熟悉,就不再贅述了。

定位布局

我們也可以通過 position 屬性來進行定位,這個大家也比較熟悉了,就不再贅述了。

flex 布局

image

僅僅通過上述的三種布局方式還是有一些缺陷,比如我們不能只使用一個屬性來實現垂直居中布局,所以就產生了第四種布局方式:flex 布局。

什么是 flex 布局

2009年,W3C 提出了一種新的方案----Flex 布局,可以簡便、完整、響應式地實現各種頁面布局。目前,它已經得到了所有瀏覽器的支持,這意味著,現在就能很安全地使用這項功能。

Flex 是 Flexible Box 的縮寫,意為"彈性布局",用來為盒狀模型提供最大的靈活性。

flex 是一種新型的布局方式,使用該布局方式可以實現幾乎所有你想要的效果。但是要注意其瀏覽器的兼容性,flex 只支持 ie 10+,所有還是要根據你的項目情況使用(沒錯,我們要求至少 ie 9,(?_?))。


flex 的瀏覽器支持情況

使用 flex 布局

flex 的使用方法很簡單,只需要將其 display 屬性設置為 flex 就可以,也可以設置行內的 flex,記得 Webkit 內核的瀏覽器,必須加上 -webkit 前綴。注意,設為 Flex 布局以后,子元素的 floatclearvertical-align 屬性將失效。

   .ele{
       display: -webkit-flex;
       display: flex;
       display: inline-flex;
       display: -webkit-inline-flex;
   }

在 flex 中,最核心的概念就是容器和軸,所有的屬性都是圍繞容器和軸設置的。其中,容器分為父容器和子容器。軸分為主軸和交叉軸(主軸默認為水平方向,方向向右,交叉軸為主軸順時針旋轉 90°)。

在使用 flex 的元素中,默認存在兩根軸:水平的主軸(main axis)和垂直的交叉軸(cross axis)
主軸開始的位置稱為 main start,主軸結束的位置稱為 main end
同理,交叉軸開始的位置稱為 cross start,交叉軸結束的位置稱為 cross end
在使用 flex 的子元素中,占據的主軸空間叫做 main size,占據的交叉軸空間叫做 cross size

flex基本概念

父容器屬性

父容器上有六個屬性

<span id="flex-direction">flex-direction 屬性</span>

flex-direction 屬性決定主軸的方向(主軸的方向不一定是水平的,這個屬性就是設置主軸的方向,主軸默認是水平方向,從左至右,如果主軸方向設置完畢,那么交叉軸就不需要設置,交叉軸永遠是主軸順時針旋轉 90°)。

.ele {
  flex-direction: row;                // 默認值,主軸為水平方向,起點在左端。
  flex-direction: row-reverse;        // 主軸為水平方向,起點在右端。
  flex-direction: column;             // 主軸為垂直方向,起點在上。
  flex-direction: column-reverse;     // 主軸為垂直方向,起點在下。
}
flex-direction 屬性

<span id="flex-wrap">flex-wrap 屬性</span>

flex-wrap 屬性決定子容器如果在一條軸線排不下時,如何換行。

.ele {
 flex-wrap: nowrap;          // 默認,不換行
 flex-wrap: wrap;            // 換行,第一行在上方。
 flex-wrap: wrap-reverse     // 換行,第一行在下方。
flex-wrap 屬性

justify-content 屬性

justify-content 屬性定義了子容器在主軸上的對齊方式。

.ele{
    justify-content: flex-start;      // 默認,左對齊
    justify-content: flex-end;        // 右對齊
    justify-content: center;          // 居中
    justify-content: space-between;   // 兩端對齊,項目之間的間隔都相等。
    justify-content: space-around;    // 每個項目兩側的間隔相等。所以,項目之間的間隔比項目與邊框的間隔大一倍。
    
}
```![justify-content 屬性](http://upload-images.jianshu.io/upload_images/4251036-84427c744d5bdaac?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

#### flex-flow 屬性
`flex-flow` 屬性是 `flex-direction` 屬性和 `flex-wrap` 屬性的簡寫形式,默認值為 `row nowrap`。

```css
.ele {
  flex-flow: <flex-direction> || <flex-wrap>;
}

align-items 屬性

align-items屬性定義自容器在交叉軸上如何對齊。
具體的對齊方式與交叉軸的方向有關,下面假設交叉軸從上到下。

.ele{
    align-items: flex-start;    // 交叉軸的起點對齊。
    align-items: flex-end;      // 交叉軸的終點對齊。
    align-items: center;        // 交叉軸的中點對齊。
    align-items: baseline;      // 項目的第一行文字的基線對齊。
    align-items: stretch;       // 默認,如果項目未設置高度或設為auto,將占滿整個容器的高度。
}
align-items 屬性

align-content 屬性

align-content 屬性定義了多根軸線的對齊方式。如果項目只有一根軸線,該屬性不起作用。

.ele{
    align-content: flex-start;   // 與交叉軸的起點對齊
    align-content; flex-end;     // 與交叉軸的終點對齊。
    align-content: center;       // 與交叉軸的中點對齊。
    align-content: space-between;// 與交叉軸兩端對齊,軸線之間的間隔平均分布。
    align-content: space-around; // 每根軸線兩側的間隔都相等。所以,軸線之間的間隔比軸線與邊框的間隔大一倍。
    align-content: stretch;     // 默認 軸線占滿整個交叉軸。
}
align-content 屬性

子容器屬性

子容器也有 6 個屬性:

  • order:子容器的排列順序
  • flex-grow:子容器剩余空間的拉伸比例
  • flex-shrink:子容器超出空間的壓縮比例
  • flex-basis:自容器在不伸縮情況下的原始尺寸
  • flex:子元素的 flex 屬性是 flex-grow,flex-shrinkflex-basis 的簡寫
  • align-self

order 屬性

order 屬性定義項目的排列順序。數值越小,排列越靠前,默認為 0。

.ele{
   order: num; 
}
order 屬性

flex-grow 屬性

flex-grow 屬性定義子容器的伸縮比例。按照該比例給子容器分配空間。

.ele{
    flex-grow: <number>; /* default 0 */
}
   
flex-grow 屬性

flex-shrink 屬性

flex-shrink 屬性定義了子容器彈性收縮的比例。如圖,超出的部分按 1:2 的比例從給子容器中減去。此屬性要生效,父容器的 flex-wrap 屬性要設置為 nowrap

.ele{
    flex-shrink: <number>; /* default 0 */
}

flex-shrink 屬性

flex-basis 屬性

flex-basis 屬性定義了自容器在不伸縮情況下的原始尺寸,主軸為橫向時代表寬度,主軸為縱向時代表高度。

.ele{
    flex-basis: <length> | auto; /* default auto */
}
flex-basis 屬性

flex 屬性

子元素的 flex 屬性是 flex-grow,flex-shrinkflex-basis 的簡寫,默認值為 0 1 auto。后兩個屬性可選。

該屬性有兩個快捷值:auto (1 1 auto) 和 none (0 0 auto)。

align-self 屬性

子容器的 align-self 屬性允許單個項目有與其他項目不一樣的對齊方式,可覆蓋父容器 align-items 屬性。默認值為 auto,表示繼承父元素的 align-items屬性,如果沒有父元素,則等同于 stretch

.ele{
    align-self: auto;             // 繼承父元素的 align-items 屬性
    align-self: flex-start;       // 交叉軸的起點對齊。
    align-self: flex-end;         // 交叉軸的終點對齊。
    align-self: center;           // 交叉軸的中點對齊。
    align-self: baseline;         // 項目的第一行文字的基線對齊。
    align-self: stretch;          // 默認,如果項目未設置高度或設為auto,將占滿整個容器的高度。
}
align-self 屬性

grid 網格布局

flex 布局雖然強大,但是只能是一維布局,如果要進行二維布局,那么我們還需要使用 grid。

grid 布局又稱為“網格布局”,可以實現二維布局方式,和之前的 表格table布局差不多,然而,這是使用 CSS 控制的,不是使用 HTML 控制的,同時還可以依賴于媒體查詢根據不同的上下文得新定義布局。

網格布局還可以讓我們擺脫現在布局中存在的文檔流限制,換句話說,你的結構不需要根據設計稿從上往上布置了。這也意味著您可以自由地更改頁面元素位置。這最適合你在不同的斷點位置實現你最需要的布局,而不再需要為響應你的設計而擔心HTML結構的問題。

table 布局不同的是,grid 布局不需要在 HTML 中使用特定的標簽布局,所有的布局都是在 CSS 中完成的,你可以隨意定義你的 grid 網格。

沒有 HTML 結構的網格布局有助于使用流體、調整順序等技術管理或更改布局。通過結合 CSS 的媒體查詢屬性,可以控制網格布局容器和他們的子元素,使用頁面的布局根據不同的設備和可用空間調整元素的顯示風格與定位,而不需要去改變文檔結構的本質內容。

瀏覽器兼容性如下:

瀏覽器兼容

grid 網格布局中的基本概念

此部分直接摘自CSS Grid布局:什么是網格布局

網格線(Grid Lines)

網格線組成了網格,他是網格的水平和垂直的分界線。一個網格線存在行或列的兩側。我們可以引用它的數目或者定義的網格線名稱。


網格線(Grid Lines)

網格軌道(Grid Track)

網格軌道是就是相鄰兩條網格線之間的空間,就好比表格中行或列。所在在網格中其分為grid column和grid row。每個網格軌道可以設置一個大小,用來控制寬度或高度。


網格軌道(Grid Track)

網格單元格(Grid Cell)

網格單元格是指四條網格線之間的空間。所以它是最小的單位,就像表格中的單元格。


網格單元格(Grid Cell)

網格區域(Grid Area)

網格區域是由任意四條網格線組成的空間,所以他可能包含一個或多個單元格。相當于表格中的合并單元格之后的區域。


網格區域(Grid Area)

使用 grid 布局

使用 grid 布局很簡單,通過display屬性設置屬性值為 grid 或 inline-grid 或者是 subgrid(該元素父元素為網格,繼承父元素的行和列的大小) 就可以了。

網格容器中的所有子元素就會自動變成網格項目(grid item),然后設置列(grid-template-columns)和 行(grid-template-rows)的大小,設置 grid-template-columns 有多少個參數生成的 grid 列表就有多少個列。

注:當元素設置了網格布局,column、float、clear、vertical-align屬性無效。

如果沒有設置 grid-template-columns,那么默認只有一列,寬度為父元素的 100%,例如

比如我們設置如下的 HTML,

<div class="grid-container">
    <div class="item item1">1</div>
    <div class="item item2">2</div>
    <div class="item item3">3</div>
    <div class="item item4">4</div>
    <div class="item item5">5</div>
    <div class="item item6">6</div>
</div>

在 CSS 中,我們不設置 grid-template-columns,只設置 grid-template-row

        .grid-container{
            display: grid;
            grid-template-rows: 50px 80px 100px;
            background: pink;
        }
        .item{
            border: 2px solid palegoldenrod;
            color: #fff;
            text-align: center;
            font-size: 20px;
        }

顯示如下


不設置 grid-template-columns

設置了 grid-template-columns 的話,設置了幾個參數,就有幾列(不超過 grid item 的個數),然后設置的 grid-template-row 參數就是每一列的高度(超出列數的高度無效)

比如:

        .grid-container{
            padding: 20px;
            display: grid;
            grid-template-rows: 50px 100px 60px 80px;
            grid-template-columns: 50px 40px 100px 80px;
            background: pink;
        }
        .item{
            border: 2px solid palegoldenrod;
            color: #fff;
        }

雖然我們設置了四個 grid-template-rows,但是因為只有兩行,所以只有前兩個值生效。效果如下:

設置 grid-template-columns

當然,我們也可以像 flex 一樣設置每一列的寬度:

        .grid-container{
            padding: 20px;
            display: grid;
            grid-template-rows: 50px 100px 60px;
            grid-template-columns: 1fr 1fr 2fr;
            background: pink;
        }

注意到我們使用了一個新的單位:fr

css fr 單位是一個自適應單位,fr單位被用于在一系列長度值中分配剩余空間,如果多個已指定了多個部分,則剩下的空間根據各自的數字按比例分配。

tips:fr 是基于網格容器可用空間來計算的(flex 也是一樣),所以我們可以和其他單位混合使用,如果需要的話

是不是找到了 flex 的感覺,這樣設置效果如下:


使用 fr 按比例設置寬度

行或列最小和最大尺寸

minmax() 函數來創建行或列的最小或最大尺寸,第一個參數定義網格軌道的最小值,第二個參數定義網格軌道的最大值。可以接受任何長度值,也接受 auto 值。auto 值允許網格軌道基于內容的尺寸拉伸或擠壓。

        .grid-container{
            padding: 20px;
            display: grid;
            grid-template-rows: minmax(100px,200px) minmax(50px,200px);
            grid-template-columns: 1fr 1fr 2fr;
            background: pink;
            height: 300px;
        }

我們將第一行的高度設置為 minmax(100px,200px),第二行的高度設置為minmax(50px,200px),容器總高度設置為 300px,這時每一列的高度要怎么算呢?

先判斷總高度是小于第一列高度的最大值和第二列高度的最大值之和的,如果大于最大值之和,那么第一列和第二列的高度都為設置的最大值,如果是小于最小值之和的話,那么第一列和第二列的高度都為設置的最小值。

現在問題來了,我們這種情況是總高度是小于第一列高度的最大值和第二列高度的最大值之和的,這樣就是先用 總高度 300px - 第一列最小高度 100px - 第二列最小高度 50px = 150px
第一列高度:第一列最小高度 100px + 150px/2 = 175px;
第二列高度:第一列最小高度 50px + 150px/2 = 125px;

效果如下:


最小和最大尺寸設置

重復行或者列

repeat() 屬性可以創建重復的網格軌道。這個適用于創建相等尺寸的網格項目和多個網格項目。

repeat() 也接受兩個參數:第一個參數定義網格軌道應該重復的次數,第二個參數定義每個軌道的尺寸。

        .grid-container{
            padding: 20px;
            display: grid;
            grid-template-columns: repeat(2,100px);
            grid-template-rows: repeat(3,100px);
            background: pink;
        }

效果如下:


repeat()

間距

grid-column-gap:創建列與列之間的距離。
grid-row-gap:行與行之間的距離。

        .grid-container{
            padding: 20px;
            display: grid;
            grid-template-columns: repeat(2,100px);
            grid-template-rows: repeat(3,100px);
            grid-column-gap: 50px;
            grid-row-gap: 15px;
            background: pink;
        }
設置間距

或者使用 grid-gapgrid-row-gapgrid-column-gap兩個屬性的縮寫。

通過網格線定位 grid item

我們可以通過表格線行或者列來定位 grid item。比如:

<div class="grid-container">
    <div class="item item1">1</div>
    <div class="item item2">2</div>
    <div class="item item3">3</div>
    <div class="item item4">4</div>
    <div class="item item5">5</div>
    <div class="item item6">6</div>
</div>
        .grid-container{
            padding: 20px;
            display: grid;
            grid-template-columns: repeat(2,100px);
            grid-template-rows: repeat(3,100px);
            grid-column-gap: 50px;
            grid-row-gap: 15px;
            background: pink;
        }
        .item{
            border: 2px solid palegoldenrod;
            color: #fff;
            text-align: center;
            font-size: 20px;
        }
        .item1{
            grid-row-start: 2;
            grid-row-end: 3;
            grid-column-start: 2;
            grid-column-end: 3;
            background: #fffa90;
            color: #000;
        }

效果:

通過網格線定位 grid item

grid-rowgrid-row-startgrid-row-end 的簡寫。grid-columngrid-column-startgrid-column-end 的簡寫。

如果只提供一個值,指定了 grid-row-startgrid-column-start 的值。

如果提供兩個值,第一個值是 grid-row-start 或者 grid-column-start 的值,第二個值是 grid-row-end 或者 grid-column-end 的值,兩者之間必須要用/隔開。

   grid-row: 2; 
   grid-column: 3 / 4;

這四個值可以用 grid-area 縮寫,分別對應 grid-row-startgrid-column-startgrid-row-endgrid-column-end

    grid-area: 2 / 2 / 3 / 3;

合并單元行與合并單元列

這個就和 excel 中的合并單元行/列是相同的(這個需要設置在 grid item 中),

    grid-column-start: 1;
    grid-column-end: 3;
    grid-row-start: 2;
    grid-row-end: 4;
    grid-row-end: 4;

也可以使用 grid-rowgrid-column 簡寫的形式,關鍵詞 span 后面緊隨數字,表示合并多少個列或行,/ 前面是從第幾行/列開始。

    grid-row: 2 / span 3; 
    grid-column: span 2;
        .grid-container{
            padding: 20px;
            display: grid;
            grid-template-columns: repeat(4,100px);
            grid-template-rows: repeat(3,100px);
            grid-column-gap: 50px;
            grid-row-gap: 15px;
            background: pink;

        }
        .item{
            border: 2px solid palegoldenrod;
            color: #fff;
            text-align: center;
            font-size: 20px;

        }
        .item1{
            grid-column-start: 1;
            grid-column-end: 3;
            grid-row-start: 2;
            grid-row-end: 4;
        }

效果如圖:


合并單元行與合并單元列

自定義網格線名稱

在 grid 中,是可以自定義網格線的名稱的,然后使用定義好的網格線來進行布局,[col1-start] 網格線名稱一定要使用 [] 括住

<div class="grid-container">
    <div class="item a">a</div>
    <div class="item b">b</div>
    <div class="item c">c</div>
    <div class="item d">d</div>
    <div class="item e">e</div>
    <div class="item f">f</div>
    <div class="item g">g</div>
    <div class="item h">h</div>
    <div class="item i">i</div>
    <div class="item j">j</div>
</div>
        .grid-container{
            text-align: center;
            height: 400px;
            padding: 100px;
            display: grid;
            grid-column-gap: 5px;
            grid-row-gap: 5px;
            background: pink;
            grid-template-columns: [col1-start] 100px [col1-end] 5px [col2-start] 100px [col2-end] 5px [col3-start]
            100px [col3-end] 5px [col4-start] 100px [col4-end];
            grid-template-rows: [row1-start] auto [row1-end] 5px [row2-start] auto [row2-end] 5px [row3-start] auto
             [row3-end] 5px [row4-start] auto [row4-end] 5px [row5-start] auto [row5-end];


        }


        .a { grid-column: col1-start / col3-end; grid-row: row1-start;
            background: #ffffff;}
        .b { grid-column: col4-start / col4-end; grid-row: row1-start / row5-end; background: orange; }
        .c { grid-column: col1-start; grid-row: row2-start; background: #ffffff;}
        .d { grid-column: col2-start; grid-row: row2-start; background: #ffffff;}
        .e { grid-column: col3-start; grid-row: row2-start; background: #ffffff;}
        .f { grid-column: col1-start / col2-end; grid-row: row3-start; background: #ffffff;}
        .g { grid-column: col3-start; grid-row: row3-start; background: #ffffff;}
        .h { grid-column: col1-start; grid-row: row4-start; background: #ffffff;}
        .i { grid-column: col2-start / col3-end; grid-row: row4-start; background: #ffffff;}
        .j { grid-column: col1-start / col3-end; grid-row: row5-start; background: #ffffff;}

最后的顯示效果是這樣的(文字和輔助線是后臺添加的):


自定義網格線名稱

通過網格區域命名和定位網格項目

什么是網格區域:

網格區域(grid-area)是一個邏輯空間,主要用來放置一個或多個網格單元格(Grid Cell)。他是由四條網格線(Grid line),網格區域每邊一條,四邊相交組織的網格軌道(Grid Track)。簡單點理解,網格區域是有四條網格線交織組成的網格空間,這個空間中可能是一個網格單元格,也可能是多個網格單元格。

定義網格區域

在CSS Grid Layout中定義網格區域有兩種方式,一種是通過網格線來定義,另一種是通過grid-template-areas來定義。接下來看看兩種定義網格區域的方法在具體使用過程中有何不同。

網格線定義網格區域

使用網格線定義網格區域的方法非常的簡單,首先依賴于 grid-template-columnsgrid-template-rows 顯式定義網格線,甚至是由瀏覽器隱式創建網格線,然后通過 grid-area 屬性通過取網格線,組成網格線交織區域,那么這個區域就是所講的網格區域。在使用 grid-area 屬性調用網格線,其遵循的規則是 grid-area: row-start/ column-start / row-end / column-end

grid-template-areas 定義網格區域

除了使用網格線的交組來定義網格區域之外,在 CSS Grid Layout 中還可以通過 grid-template-areas 屬性來定義網格區域的名稱,然后需要放在對應網格區域的元素,可以通過 grid-area 屬性來指定。而且重復區域可以使用同一個名稱來實現跨區域。另外對于空的軌道區域,可以使用點號 . 來代表

<div class="grid-container">
    <div class="header ">header</div>
    <div class="content ">content</div>
    <div class="sidebar ">sidebar</div>
    <div class="footer ">footer</div>
</div>
        .grid-container{
            text-align: center;
            padding: 20px;
            display: grid;
            grid-column-gap: 5px;
            grid-row-gap: 5px;
            background: pink;
            grid-template-areas: "header header header header header"
                                 "sidebar content content content content"
                                 "footer footer footer footer footer";

            grid-template-rows: 50px 150px 50px;
            grid-template-columns: 200px 200px 200px;

        }

        .header { grid-area:header; background: #fff}
        .content { grid-area: content; background: #fffa90}
        .sidebar { grid-area: sidebar; background: #5bc0de}
        .footer { grid-area: footer; background: #ffff00}
grid-template-areas 定義網格區域

我發現這樣布局的一個優點,在不設置高度的情況下(父容器和 grid-template-rows 的值,或者 grid-template-rows 設置為 auto 時,slidercontent 的高度是一致的,并且會根據其內的高度自適應)

例如:


不設置高度使高度自適應

常用的 CSS 布局

在介紹了 CSS 的布局方式之后,我們來看一下常用的 CSS 布局有哪些

水平垂直居中(感覺總結的并不是很好)

感覺垂直居中真的是已經被講爛了,但是在平時做項目時,我都是靠試的,導致面試的時候被問到有時候回答不上來,現在就用自己的方式來總結一下。其實這個方式是有很多的,但就是看的教程太多了,導致最后一個都沒有記住,所以我決定盡可能的將情況考慮完整,然后每一種情況只記住一個最佳實踐。

對于居中,我個人認為不需要背什么“x 種方式實現 xx”這樣的例子,我們只需要了解其原理即可寫出符合要求的 css。

水平、垂直居中,個人比較喜歡用絕對定位的方法實現,其次就是使用 table 布局,因為自帶垂直居中。如果是單行的行內元素使用 line-height 等于 height,對于多行元素的垂直居中,大部分都是使用 table 元素(求推薦更好的布局),當然還有 flex 和 grid 布局。

水平居中

一般水平居中還是比較容易的,我一般都是先看子元素是固定寬度還是寬度未知

固定寬度

這種方式是絕對定位居中,除了使用 margin,我們還可以使用 transform(注意瀏覽器兼容性,只適用于 ie9+,移動開發請忽略)

        .container{
            width: 300px;
            height: 200px;
            background: pink;
            position: relative;
        }
        .inner{
            width: 100px;
            height: 50px;
            position: absolute;
            top: 50%;
            left: 50%;
            margin-top: -25px;
            margin-left: -50px;
            background: #fff;
            text-align: center;
        }
        .container{
            width: 300px;
            height: 200px;
            background: pink;
            position: relative;
        }
        .inner{
            width: 100px;
            height: 50px;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: #fff;
            text-align: center;
        }

或者使用 magin:0 auto;但一般情況下我都會使用上一種,因為習慣了(?_?)

寬度未知

將子元素設置為行內元素,然后父元素設置 text-align: center

        .container{
            width: 300px;
            height: 200px;
            background: pink;
            position: relative;
            text-align: center;
        }
        .inner{
            display: inline-block;
            
        }

多個塊狀元素

上面的方式即使子元素不止一個也想實現水平居中也是有效的,(寬度固定不固定都可,不固定的話就不需要設置寬度,會被自動撐開,但是要考慮到撐爆的情況)例如:

        .container{
            width: 250px;
            height: 200px;
            background: pink;
            position: relative;
            text-align: center;
            padding: 20px;
        }
        .inner{
            display: inline-block;
            width: 50px;
            height: 150px;
            margin: 0 auto;
            background: #fff;
            text-align: center;
        }
多個子元素水平居中

當然也可以使用我們剛剛介紹的 flex,我們只需要讓子元素在主軸上的對齊方式設置為居中就可以

        .container{
            width: 250px;
            height: 200px;
            background: pink;
            display: flex;
            justify-content: center;
            padding: 20px;
        }
        .inner{
            background: #fff;
            width: 50px;
            height: 150px;
            margin-left: 10px;
        }

垂直居中

單行行內元素

單行海內元素居中,只需要將子元素的行高等于高度就可以了。

        #container {
            height: 400px;
            background: pink;
        }
        #inner{
            display: inline-block;
            height: 200px;
            line-height: 200px;
        }

多行元素

上面的這種方式只能處理單行的行內元素,對于多行的行內元素是處理不了的,因為給每一個子元素都設置了 line-height,看了很多方法,要不是沒有效果,要不然就是又局限性,提到最多的是使用 table-cell 的方式(但是貌似這個方法也有一點弊端,那就是其子元素的表現形式和行內元素類似,子元素不能獨占一行),當然如果你有更好的方式,歡迎提出

        .container {
            width: 200px;
            height: 400px;
            background: pink;
            position: absolute;
            display: table;
            vertical-align:middle;
        }

        .inner{
            display: table-cell;
            vertical-align:middle;
        }

還有一個方法是設置一個空的行內元素,使其 height:100%display:inline-block,vertical-align: middle; 并且 font-size:0。但是這樣方式的原理我還不是很清楚,只是知道要設置一個空元素,高度和父元素相等,并且設置垂直居中的屬性。但是,這只是用與所有的行內元素的寬度和不超過父元素的寬度的情況。

<div class="container">
    ![](WechatIMG110.jpg)
    ![](WechatIMG110.jpg)
    ![](WechatIMG110.jpg)
    <p>123</p>
</div>
        .container{
            width: 400px;
            height: 100px;
            background: pink;
            text-align: center;

        }
        img{
            vertical-align: middle;

        }
        p{
            display: inline-block;
            height: 100%;
            line-height: 100%;
            vertical-align: middle;
            font-size: 0;
        }

效果:


多行行內元素

另一個一勞永逸的方法就是 flex,但是要注意瀏覽器的兼容性。

圖片和文字垂直居中

經常有看到設計稿是圖片和文字垂直居中的,那么怎么才能讓圖片和文字垂直居中呢?
只需要給圖片一個 vertical-align: middle; 屬性就可以:

<div class="container">
    ![](WechatIMG110.jpg)
    <p>123456</p>

</div>
        .container {
            background: pink;
            padding: 20px;
            height: 400px;

        }
        .container img{
            display: inline-block;
            vertical-align: middle;
        }

        .container p{
            display: inline-block;

        }

效果如下:


圖片和文字垂直居中

重點來啦~

自我感覺總結的居中問題不是特別的好,我們現在可以來總結一下其中的原理:

  • 我們要實現水平或者垂直居中,應該從兩方面下手:元素自帶居中的效果或者強制讓其顯示在中間。
  • 所以我們先考慮,哪些元素有自帶的居中效果,最先想到的應該就是 text-align:center 了,但是這個只對行內元素有效,所以我們要使用 text-align:center 就必須將子元素設置為 display: inline; 或者 display: inline-block;
  • 接下來我們可能會想既然有 text-align 那么會不會對應也有自帶垂直居中的呢,答案是有的 vertical-align:,我一直不是很喜歡使用這個屬性,因為十次用,9.9 次都沒有垂直居中,一度讓我懷疑人生。現在貌似也搞得不是很清楚,看了 張鑫旭的文章 居然看得也不是很懂,笑哭。目前就在 table 中設置有效,因為 table 元素 的特性,打娘胎里面帶的就是好用。還有一種可以有效的方式是前面提到的空元素的方式,不過感覺多設置一個元素還不如使用 table
  • 還有一只設置垂直居中的是將行內元素的 line-heightheight 設置為相同(只適用于單行行內元素)
  • 固定寬度或者固定高度的情況個人認為設置水平垂直居最簡單,可以直接使用絕對定位。使用絕對定位就是子元素相對于父元素的位置,所以將父元素設置 position:reletive 對應的子元素要設置 position:absolute,然后使用 top:50%;left:50%,將子元素的左上角和父元素的中點對齊,之后再設置偏移 margin-top: 1/2 子元素高度;margin-left: 1/2 子元素寬度;。這種方式也很好理解。
  • 上面的絕對定位方法只要將 margin 改為 transform 就可以實現寬度和高度未知的居中(兼容性啊兄弟們!(?_?))transformX:50%;transformY:50%

不行,感覺總結的還是很渣,╮(╯▽╰)╭哎,誰有好的方法,求推薦。

圣杯布局

其實我還真是第一次聽說圣杯布局這種稱呼,看了下這個名字的由來,貌似和布局并沒有什么關系,圣杯布局倒是挺常見的三欄式布局。兩邊頂寬,中間自適應的三欄布局。
效果如下:


圣杯布局

這個布局方式的關鍵是怎么樣才能使得在伸縮瀏覽器窗口的時候讓中間的子元素寬度改變。可以適應瀏覽器的寬度變化使用百分比設置寬度再合適不過,所以我們要將中間子元素的寬度設置為 100%,左邊和右邊的子元素設置為固定的寬度。

我們就來實現一下這樣的布局:

給出HTML結構

HTML 文件就很普通:

<div class="container">
    <div class="middle">測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試</div>
    <div class="left">left</div>
    <div class="right">right</div>
</div>

這里我們要注意的是,中間欄要在放在文檔流前面以優先渲染。

給出每個子元素的樣式

然后我們寫 CSS,我們現將其三個元素的寬度和高度設置好,然后都設置為 float:left:

        .middle{
            width: 100%;
            background: paleturquoise;
            height: 200px;
            float: left;
        }
        .left{
            background: palevioletred;
            width: 200px;
            height: 200px;
            float: left;
            font-size: 40px;
            color: #fff;
        }
        .right{
            width: 200px;
            height: 200px;
            background: purple;
            font-size: 40px;
            float: left;
            color: #fff;
        }

這時的效果如下:


圣杯布局

使子元素在同一行顯示

我們可以看出,現在三個子元素是在一排顯示的,因為我們給中間的子元素設置的寬度是 100%,并且中間的子元素在文檔流的最前面,最先被渲染。

那么我們要使得三個元素在同一排顯示。接下來我們要將 .left.right 向上提。實際上我們是使用 margin-left 為 負值來實現的,我們將 .leftmargin-left 設置為 -100%(負的中間子元素的寬度),這樣,左邊的元素就會被“提升”到上一層。

然后就是右邊子元素了,只需要設置 margin-left 設置為負的自身的寬度。

結果如下:


這里寫圖片描述

使得中間子元素不被遮蓋

從上一張截圖顯示中顯示中間的子元素被遮擋了,所以說我們要解決這個問題,要怎么解決呢?嗯... 只要使得中間的子元素顯示的寬度剛好為左邊元素和右邊元素顯示中間的寬度就可以。同時我們還必須保證是使用的半分比的布局方式。

這樣的話有一種方式可以即使中間的寬度減少,又可以使中間的寬度仍然使用 100%,那就是設置父元素的 padding 值,將父元素的 padding-left 設置為左邊子元素的寬度,將父元素的 padding-right 設置為右邊子元素的寬度。

顯示效果如下:


圣杯布局

將左邊和右邊的子元素像兩邊移動

嗯... 這貌似也不是我們想要的效果,但是,中間的子元素確實是在中間了,那么我們只需要設置相對位置,將左邊的子元素和右邊的子元素向兩邊移動就好。

最終的 CSS 代碼如下:

        .container{
            padding: 0 200px;
        }
        .middle{
            width: 100%;
            background: paleturquoise;
            height: 200px;
            float: left;
        }
        .left{
            background: palevioletred;
            width: 200px;
            height: 200px;
            float: left;
            font-size: 40px;
            color: #fff;
            margin-left:-100%;


        }
        .right{
            width: 200px;
            height: 200px;
            background: purple;
            font-size: 40px;
            float: left;
            color: #fff;
            margin-left:-200px;
            
        }

最終效果如下:


圣杯布局

雙飛翼布局

其實雙飛翼布局是為了解決圣杯布局的弊端提出的,如果你跟我一起將上面的圣杯布局的代碼敲了一遍,你就會發現一個問題,當你將瀏覽器寬度縮短到一定程度的時候,會使得中間子元素的寬度比左右子元素寬度小的時候,這時候布局就會出現問題。所以首先,這提示了我們在使用圣杯布局的時候一定要設置整個容器的最小寬度。

圣杯布局弊端

雙飛翼和圣杯布局區別

圣杯布局和雙飛翼布局解決問題的方案在前一半是相同的,也就是三欄全部float浮動,但左右兩欄加上負margin讓其跟中間欄div并排,以形成三欄布局。

不同在于解決”中間欄div內容不被遮擋“問題的思路不一樣:圣杯布局,為了中間div內容不被遮擋,將中間div設置了左右padding-left和padding-right后,將左右兩個div用相對布局position: relative并分別配合right和left屬性,以便左右兩欄div移動后不遮擋中間div。

雙飛翼布局,為了中間div內容不被遮擋,直接在中間div內部創建子div用于放置內容,在該子div里用margin-left和margin-right為左右兩欄div留出位置。

作者:知乎用戶
鏈接:https://www.zhihu.com/question/21504052/answer/50053054
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

所以只是一個小小的改動

<div class="container">
    <div class="middle-container">
        <div class="middle">測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試</div>

    </div>
    <div class="left">left</div>
    <div class="right">right</div>
</div>
        .middle-container{
            width: 100%;
            background: paleturquoise;
            height: 200px;
            float: left;

        }

        .middle{
            margin-left: 200px;
            margin-right: 200px;
        }
        .left{
            background: palevioletred;
            width: 200px;
            height: 200px;
            float: left;
            font-size: 40px;
            color: #fff;
            margin-left:-100%;


        }
        .right{
            width: 200px;
            height: 200px;
            background: purple;
            font-size: 40px;
            float: left;
            color: #fff;
            margin-left:-200px;

        }

這樣,在我們將中間元素寬度調到比兩邊元素小的時候,也是可以正常顯示,但是如果總寬度小于左邊元素或者右邊元素的時候,還是會有問題。


雙飛翼布局

提問

如果你已經看到了這里,那么很感謝,已經到最后了,我承認,這篇文章真的很長...

所以,來,打起精神,對于下面的布局方式,大家都什么好的布局方式或者奇淫技巧呢~


布局方式

其實我只是想知道左邊頭像和下面的文字垂直居中有什么好的方法。

此問題可以簡化為,多個未知高度的元素如何垂直居中?對了,不使用 flex 和grid 布局,要求兼容 ie8+。

大家捧個場,可以直接發郵件將你的代碼發給我,我郵件地址:991939332@qq.com,謝謝大家,謝謝謝謝

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,887評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,914評論 2 372

推薦閱讀更多精彩內容

  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標簽默認的外補...
    _Yfling閱讀 13,775評論 1 92
  • 簡介 CSS Grid布局 (又名"網格"),是一個基于二維網格布局的系統,旨在改變我們基于網格設計的用戶界面方式...
    咕咚咚bells閱讀 2,530評論 0 4
  • 簡介CSS網格布局(又稱“網格”),是一種二維網格布局系統。CSS在處理網頁布局方面一直做的不是很好。一開始我們用...
    _leonlee閱讀 65,124評論 25 173
  • 選擇qi:是表達式 標簽選擇器 類選擇器 屬性選擇器 繼承屬性: color,font,text-align,li...
    wzhiq896閱讀 1,779評論 0 2
  • 難忘的一天。 今天體驗了“小時工”。 上午到店里,聞姐說接了一小時工到活兒,可是打了兩本登記記錄也沒找到人,說實在...
    漫漫無憂閱讀 308評論 14 5