一些面試時關於 CSS 的問題

1. 水平垂直居中問題

這能夠說是最經典的問題了,水平垂直居中,這個問題從入門前端一直到面試,甚至到工做以後都會時不時遇到,最近的面試也被問過這之類的問題,這裏仍是好好總結一番,以做備忘。公用 HTML 部分:css

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>CSS居中</title>
</head>
<body>
<!-- <div class="grandfather"> -->
    <div id="parent">
        <div id="child"></div>
    </div>
<!-- </div> -->
</body>
</html>

方法一:

設置子元素 position 爲 absolute ,而後讓 top 和 left 都爲 50%,再使 margin 爲子元素的長和寬的負 1/2 便可:html

說明: 這種方法侷限性很大,不作推薦。
注意: 此處父元素得添加 postion:relative/absolute ,由於 absolute 定位是基於最近的設有 position 的父元素進行定位,若是父元素沒有設置 position ,則其會基於 body 定位。可是若是 body 也沒有的設置 position 的話則基於視口高度計算位置:document.documentElement.clientHeight 。能夠試試去掉 parent 的 position 會發生很奇妙的事呢!前端

附加: 關於子元素 position 設置爲 relative 而引起的問題。若是此時父元素的 position 爲 absolute,那麼不會有什麼奇怪的現象發生;可是若是此時父元素的 position 爲 relative ,那麼你會發現父元素會向上方移動一點距離,這又是爲何呢???這涉及到外邊距塌陷(margin-collapse)問題了,這點在後面的一些2. 外邊距問題中去解釋了(提早透露:父級向上移動了-50px)。
推薦指數: ★git

#parent {
    background-color: black;
    position: relative;        /*或者absolute*/
    height: 300px;
    width: 300px;
}

#child {
    background-color: #ccc;
    position: absolute;
    left: 50%;
    top: 50%;
    margin: -50px 0 0 -50px;
    height: 100px;
    width: 100px;
}

方法二:

CSS裏面還有一種 display: table-cell 的屬性,咱們能夠將父元素的 display 設置爲 table-cell 屬性,而後將子元素的 display 設置爲 inline 或者 inline-block ,此時即可以用咱們熟悉的 text-align: center 和 vertical-align: middle 來使得子元素水平垂直居中了。github

說明: 這種方法最適合子元素都爲行內元素(或者帶有行內元素性質的塊級元素)的佈局,故而能夠根據狀況選用。web

推薦指數: ★★★☆面試

#parent {
    background-color: black;
    display: table-cell;
    text-align: center;
    vertical-align: middle;
    height: 300px;
    width: 300px;
}

#child {
    background-color: #ccc;
    display: inline-block;
    height: 100px;    /*能夠改成33.3%*/
    width: 100px;    /*能夠改成33.3%*/
}

方法三:

方法一中只能解決子元素定大小問題,有時候子元素大小變化能夠用如下方法解決:瀏覽器

說明: 這種方法很是棒,兼容性也很不錯,強推!app

附加: 關於子元素的 position 設爲 relative 而使得垂直居中無效問題。關於這個問題其實很好理解,由於,postion: relative 的移動是基於自身本來的位置嘛,top 、 bottom 、 left 、 right 都爲 0 ,換句話說就是位置不動,然而,你有見過塊級元素可以 margin: auto 上下自動補齊的嗎!沒有吧~ 故而起做用的只有左右自動計算補齊而已,也就只有水平居中對齊了!
推薦指數: ★★★★★ide

#parent {
    background-color: black;
    position: relative;
    height: 300px;
    width: 300px;
}

#child {
    background-color: #ccc;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
    height: 33.3%;
    width: 33.3%;
}

方法四:

因爲CSS3的來臨,咱們也應該跟上時代的潮流,故而對上述方法有有所改進,在此咱們能夠嘗試一下 transform 元素的 translate 2D 平移,讓其想着 X 、Y 軸負方向移動自身長度的一半距離便可達到效果。

說明: 畢竟技術向新的方向發展,能夠多嘗試一下新技術,推薦。

注意: 因爲瀏覽器的支持性問題,使用的時候能夠檢測 CSS 支持性,也能夠經過 Autoprefixer CSS online 來寫兼容性代碼。
附加: 關於 CSS3 開啓 GPU 硬件加速提高網站動畫渲染性能問題。這一點不知道對此位置平移轉換有沒幫助,不過當作是拓展來介紹了,在此我就不詳細說了,推薦 CSS3 頁面渲染加速
推薦指數: ★★★★

#parent {
    background-color: black;
    position: relative;
    height: 300px;
    width: 300px;
}

#child {
    background-color: #ccc;
    position: absolute;        /*或者relative*/
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    height: 33.3%;
    width: 33.3%;
}

方法五:

這個是另一種 CSS3 的解決方法(我比較喜歡),即 flex 佈局。經過設置父元素的 display 屬性爲 flex,而後設置其 align-content 和 justify-content 來使得子元素可以水平垂直居中。

說明: 這個方法在佈局上很是靈活,在不考慮兼容的狀況下,強烈推薦此方法進行佈局。這真的是個很是棒的佈局方法!

注意: 因爲瀏覽器的支持性問題,使用的時候能夠檢測 CSS 支持性,也能夠經過 Autoprefixer CSS online 來寫兼容性代碼。
附加: 關於flex的詳細介紹能夠參考阮老師的博客: Flex 佈局語法篇 Flex 佈局實例篇
推薦指數: ★★★★☆

#parent {
    background-color: black;
    display: flex;
    display: -webkit-flex;  /*Safari*/
    align-content: center;
    -webkit-align-items: center;
    justify-content: center;
    -webkit-justify-content: center;
    height: 300px;
    width: 300px;
}

#child {
    background-color: #ccc;
    height: 33.3%;
    width: 33.3%;
}

2. 外邊距問題

現象描述

第一種情形是,當你在一個 div 元素內插入一個塊級子元素,而後設置其 margin-top 值,這是你會發現並非子元素在父元素內撐開了一段距離,而是父元素向上撐開了一段距離。

原覺得:                            實際上:
       body                                body
--------------------               --------------------
      parent                                *
    -----------                             | 50px
         *                                  *
         | 50px                           parent
         *                             -----------
       child                               child
    -----------                        -----------
      parent                              parent
--------------------               --------------------
       body                                body

另外一種狀況是,當你在 div 元素內插入多個塊級元素,你給其中相鄰的兩個設置 margin-top 和 margin-bottom ,你會發現,他們之間的距離爲相對應的 margin-top 和 margin-bottom 中的最大值,當設置第一個塊級元素的 margin-top 屬性則會像第一種情形那樣,父級框移動了。

原覺得:
        parent
------------------------
          | 10px
        childA
          | 10px ==> | 30px
          | 20px ==> |
        childB
          | 20px
------------------------
        parent

實際上:
        body
--------------------------
          | 10px
        parent
    ----------------
        childA
          | max(10px, 20px) ==> 20px
        childB
          | 20px
    ----------------
        parent
--------------------------
        body

問題分析

詳細解答在官方文檔的 8.3.1 合併 margin 這一節,太長了,我就不復制了,可是簡單來講能夠用W3C上的話來總結:

外邊距合併指的是,當兩個垂直外邊距相遇時,它們將造成一個外邊距。

合併後的外邊距的高度等於兩個發生合併的外邊距的高度中的較大者。

這樣就清楚了!下面說說個人理解(很實用):

  1. Case 1:
    父親與兒子並排站(沒邊界嘛,父親位置不定),兒子說:我站在相對前方目標 1m 的地方,問:父親站在離前方目標多遠處?

顯然是 1m 的地方嘛!由於目標不明確(父親也是相對嘛),故而父親一樣也是站在相對前方 1m 處。

  1. Case 2:
    ChildA: 我站在距離 ChildB 1m 的位置。

ChildB: 我站在距離 ChildA 1m 的位置。
問: ChildA、ChildB距離多遠?
根據相對性,這不就是 1m 嘛!
依次類比,很類似吧(沒邊界擋着 == 站在同一塊兒跑線)! (σ゚∀゚)σ..:*☆ 哎喲不錯哦!!!

問題解決

其實分析時已經代表了,由於都站在同一塊兒跑線,故而你們都不分前後,要讓它們分開,設置點障礙就好了,如:
狀況 1:
Method 1: 給父元素添加 border: 1px solid #xxxxxx(劃分界限,父子沒並排站,兒子前方有了目標)。
Method 2: 給父元素添加 overflow: hidden 屬性(至關於父級給本身定了一個隱藏邊界)。
Method 3:讓父元素爲絕對定位(由於至關於讓父元素站在一個定點,子元素的前方目標明確了,是父親相對本身距離爲 0,距離也就拉開了)。
Method 4:爲父元素聲明浮動(浮動會脫離文檔流,此時瀏覽器會給頂元素位置,即所能達到的最左上方)。

我的推薦: Method 2。



這個狀況能夠說是前一個狀況的完整版,其實總體上就是BFC問題(前者也是),BFC的詳細內容我就不細說了,推薦 BFC 神奇背後的原理
狀況 2:
首先得讓父級元素按照狀況 1中方式處理,其次是處理子元素。
Method 1:本身計算好相鄰元素的距離,而後直接設置。
Method 2:給每一個子元素添加一個 wrapper ,使得每一個子元素都是 BFC 區域。

我的推薦: Method 2。

3. 奇怪的佈局問題

現象描述

當你在 div 元素內插入多個行內塊級元素,你給其中任意一個或者多個設置 margin-top 想要使得它/它們表現得不同凡響,但是,到頭來全部元素都會移動,並且惟一準確的只有所設值最大的那個子元素,其餘子元素則混淆。

(注:全部子元素高度爲 height: 50px)
原覺得:
                      parent
----------------------------------------------------
        | 50px        childB               | 25px
      childA                            childC
----------------------------------------------------
                      parent

實際上:
                      parent
----------------------------------------------------
        | 50px          | 50px + 25px      | 50px - 25px
      childA          childB            childC
----------------------------------------------------
                      parent

問題分析

其實這個問題和IFC問題很類似,也就是行內元素基線的選擇問題。 IFC 的介紹中有這麼一段話:

IFC ( Inline Formatting Contexts )直譯爲"內聯格式化上下文",IFC 的 line box(線框)高度由其包含行內元素中最高的實際高度計算而來(不受到豎直方向的 padding/margin 影響)

IFC 中的 line box 通常左右都貼緊整個 IFC ,可是會由於 float 元素而擾亂。

這就說到很明白了。因爲這裏是 display: inline-block 故而,其線框高度包含了 margin 、 border 、 padding 、 content ,這樣就知道爲何其餘元素的 margin 後的結果與預期不一致的問題了。(此處把 childC 的高度變爲 300xp 則是以 childC 的基線爲基準定位其餘兄弟元素,由於此時 childC 「最高」)。

友情推薦: 什麼是 BFC 、IFC 、GFC 和 FFC

問題解決

要解決這個問題,想讓不一樣子元素呈現不一樣效果,咱們能夠給每一個子元素添加一個 wrapper ,並讓 wrapper 以 BFC 的形式包裹子元素,而後你就能夠肆無忌憚的改變每一個子元素位置了!(參考1. 水平垂直居中問題

/*wrapper style*/
.wrapper {
    display: inline-block;
    height: 100%;       /*讓 wrapper 基線對齊*/
    overflow: hidden;   /* BFC */
}

其餘問題: 爲何其餘子元素的 margin: mpx 不是在基線上位置向下移動 mpx 而是向上移動呢?(m > 0)

這個問題其實也很好說明,因爲此處默認對齊方式是都是基於 bottom 的,故而都是在 bottom 這條起始線開始變化(能夠打開瀏覽器看盒子模型), childB 因爲沒有 margin 故而其底邊線爲此時公用的底邊界,其餘盒子在此基礎上有 margin-bottom 的開始向上移動(「最高」的盒子會撐高整個高度,其它盒子只會上升 margin-bottom 值),所以會出現此現象。

此題代碼

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>CSS外邊距塌陷</title>
</head>

<style type="text/css">
    #parent {
        background-color: black;
        height: 500px;
        overflow: hidden;
        width: 500px;
    }

    #parent div {
        display: inline-block;  /*包含了 wrapper */
    }

    #childA {
        background-color: greenyellow;
        height: 100px;
        margin: 25px 0;
        width: 100px;
    }

    #childB {
        background-color: aliceblue;
        height: 100px;
        width: 100px;
    }

    #childC {
        background-color: orangered;
        height: 100px;
        margin: 50px 0;
        width: 100px;
    }

    .wrapper {
        height: 100%;
        overflow: hidden;
    }
</style>

<body>
    <div id="parent">
        <div class="wrapper">
            <div id="childA"></div>
        </div>
        <div class="wrapper">
            <div id="childB"></div>
        </div>
        <div class="wrapper">
            <div id="childC"></div>
        </div>
    </div>
</body>
</html>
相關文章
相關標籤/搜索