天天看點

button是塊級元素嗎_[重拾CSS]一道面試題來看僞元素、包含塊和高度坍塌

前言

前幾天某個群友在群裡問了一道面試題,就是關于一個自适應的正方形布局的困惑,先貼上代碼。我其實很長一段時間沒有寫 CSS 了,對于裡面的一些細節也比較模糊了,是以決定重拾 CSS,來重新捋一捋這題目中的一些知識點。(本文大多采用的講解方式為 w3 的 CSS 标準 + MDN,如果對标準比較熟悉的大神請跳過這篇文章)

通過标準分析有什麼好處?最權威的解答,能夠少走彎路,不會出錯。

代碼:

效果(https://codepen.io/hua1995116/pen/WNQyKBY):

button是塊級元素嗎_[重拾CSS]一道面試題來看僞元素、包含塊和高度坍塌

image-20200517160937926

上面的實作看似簡單,但是我們去深究,大約會冒出以下三個問題。

  • ::after

    僞元素有什麼特殊的魔法嗎?
  • margin-top:100%

     為什麼能夠自适應寬度?
  • overflow:hidden

    在這裡是什麼作用?

是以我們會按照上述疑問來逐一講解。

本文所有 demo 都存放于 https://github.com/hua1995116/node-demo/tree/master/css-margin

::after

僞元素有什麼特殊的魔法嗎?

::after

說到

::after

那就需要說到僞元素,我們先來看看僞元素的定義吧。

僞元素(Pseudo elements)表示文檔的抽象元素,超出了文檔語言明确建立的那些元素。

—— https://www.w3.org/TR/css-pseudo-4/#intro

再來看看

::after

的特性:

When their computed content value is not none, these pseudo-elements generate boxes as if they were immediate children of their originating element, and can be styled exactly like any normal document-sourced element in the document tree.

根據描述來看,當僞元素

content

不為

none

的時候,我們可以把他們當做正常的元素一樣來看待。

是以我們的示例轉化為更加通俗易懂的樣子。

既然說到了僞元素,我們也順便回顧一下僞類(Pseudo classes),他們的文法非常類似,卻是水和水銀的關系。

僞類

是添加到選擇器的關鍵字,指定要選擇的元素的特殊狀态。

:

單冒号開頭的為僞類,代表形态為

:hover

僞元素

表示文檔的抽象元素,超出了文檔語言明确建立的那些元素。(因為它們并不局限于适應文檔樹,是以可以使用它們來選擇和樣式化文檔中不一定映射到文檔樹結構的部分。)

::

雙冒号開頭的為僞元素,代表形态為

::after

僞元素是應用于元素

/* 每一個 
           

 元素的第一行。 */

p::first-line {   color: blue;   text-transform: uppercase; }

僞類和僞元素差別

通過上述,我們可能大緻了解了但是對于一些看上去用途很相似的僞類和僞元素還是有點迷糊。

「關鍵差別: 是否建立了一個超出文檔樹以外的元素。」

我們乍一看

::first-line

的效果不是和

:first-child

一樣嘛?來舉一個例子。

button是塊級元素嗎_[重拾CSS]一道面試題來看僞元素、包含塊和高度坍塌

image-20200517185343290

像我剛才所說乍一看,兩種效果是一樣的。但是他們真的一樣麼?當然不是!我們給他們加上寬度。

::first-line

的形态

button是塊級元素嗎_[重拾CSS]一道面試題來看僞元素、包含塊和高度坍塌

image-20200517185652868

在真實的渲染中我們可以了解為

但是我們在真實的 DOM Tree 是看不到的。這一點規範中也說明了,因為它們并不單單适用于文檔樹,是以使用它們來選擇和樣式化文檔不一定映射到文檔樹。

Since they are not restricted to fitting into the document tree, they can be used to select and style portions of the document that do not necessarily map to the document’s tree structure.

:first-child

的形态

button是塊級元素嗎_[重拾CSS]一道面試題來看僞元素、包含塊和高度坍塌

image-20200517185714812

小結

至此我們搞清楚了我們的第一個問題,

::after

沒有魔法,在本題可以将它當成正常的元素,并且我們搞清楚了僞元素和僞類的差別。

margin-top:100%

 為什麼能夠自适應寬度?

現在我們已經将這個示例轉化成一個比較簡單的形态,沒有過多的知識。

然後我們來看看這個

margin-top: 100%

,看上去他相對于了父元素的

width

值來進行計算的。那麼我們來看看

margin-top

到底是怎麼計算的。

https://www.w3.org/TR/CSS22/box.html#margin-properties

button是塊級元素嗎_[重拾CSS]一道面試題來看僞元素、包含塊和高度坍塌

image-20200517192743042

可以看到

margin-top

主要有三種形态。第一種是固定值,第二種為百分比,第三種為 auto,這裡我們主要來看下 百分比的計算。

通過上述的描述,可以知道

margin-top

margin-bottom

margin-left

margin-right

百分比的長度是由目前元素的包含塊的寬度來決定的。

包含塊(Containing blocks)

那麼什麼是

包含塊(Containing blocks)

呢?

The position and size of an element's box(es) are sometimes calculated relative to a certain rectangle, called the containing block of the element.

—— https://www.w3.org/TR/CSS22/visudet.html#containing-block-details

元素盒子的位置和大小有時是相對于某個矩形計算的,稱為元素的包含塊。

上述的描述有點拗口,我們大緻隻需要知道它就是一個矩形的塊。下面重要的來了,包含塊是怎麼确定的?(https://developer.mozilla.org/zh-CN/docs/Web/CSS/All_About_The_Containing_Block)

确定一個元素的包含塊的過程完全依賴于這個元素的

position

屬性:

  1. 如果 position 屬性為

    static

    relative

    sticky

    ,包含塊可能由它的最近的祖先「塊元素」(比如說inline-block, block 或 list-item元素)的内容區的邊緣組成,也可能會建立格式化上下文(比如說 a table container, flex container, grid container, 或者是 the block container 自身)。
  2. 如果 position 屬性為

    absolute

    ,包含塊就是由它的最近的 position 的值不是

    static

    (也就是值為

    fixed

    ,

    absolute

    ,

    relative

    sticky

    )的祖先元素的内邊距區的邊緣組成。
  3. 如果 position 屬性是 「

    fixed

    」,在連續媒體的情況下(continuous media)包含塊是 viewport ,在分頁媒體(paged media)下的情況下包含塊是分頁區域(page area)。
  4. 如果 position 屬性是absolute 或fixed,包含塊也可能是由滿足以下條件的最近父級元素的内邊距區的邊緣組成的:
    1. A

      transform

      or

      perspective

      value other than

      none

    2. A

      will-change

      value of

      transform

      or

      perspective

    3. A

      filter

      value other than

      none

      or a

      will-change

      value of

      filter

      (only works on Firefox).
    4. A

      contain

      value of

      paint

      (例如:

      contain: paint;

      )

注意,以下所有例子的視口寬度都為 594px

Case1

第一種情況,就是我們的例子的情況,目前元素的 position 沒有填寫,預設為 static 。是以滿足第一種情況,取它最近的祖先元素,也就是包含塊為 container.

是以

inner

的 「margin-top」 = 父元素container = 視窗寬度(594px) * 30% = 178.188px。

Case2

目前元素為

position:absolute

, 是以擷取的最近的一個

position

static

的元素

這個時候

inner

的 「margin-top」 = outer 的寬度(500px)* 100% = 500px。

Case3

目前元素為

position:fixed

 ,此時的包含塊為視口。

是以這個時候 「margin-top」 = viewport 的寬度(594px)* 100% = 594px。此時是無關父元素,以及無關外層position 的設定的。

Case4

在 case2 和 case 3 的基礎上,會有一些特例影響包含塊的尋找。主要就以下4種情況

  1. A

    transform

    or

    perspective

    value other than

    none

  2. A

    will-change

    value of

    transform

    or

    perspective

  3. A

    filter

    value other than

    none

    or a

    will-change

    value of

    filter

    (only works on Firefox).
  4. A

    contain

    value of

    paint

    (例如:

    contain: paint;

    )

我舉一個

transform

例子來講解。

這個時候我們的計算又發生了變化,此時包含塊又變成了

container

.

「margin-top」 = 父元素container = 視窗寬度(594px) * 30% = 178.188px。

小結

是以對于我們一開始的問題,就是我們的 Case1,采取的就是最近的父元素。是以 margin-top 就是 父元素

square1

的寬度,是以實作了一個自适應的正方形。

對于 position 的不同形态,對于布局狀态的影響,一般在我們入門 css 的時候就學了,但是可能沒有那麼仔細去了解每種情況,也可能不知道他的名詞,叫做

包含塊

,這次我們對它進行了梳理,這一節就這樣結束,繼續看!

overflow:hidden

在這裡是什麼作用?

假如我們把

overflow:hidden

去了。

我們可以看到以上執行完顯示出現的畫面為一篇空白。此時我們就要引出了我們的最後一個概念就是,「邊距坍塌(Collapsing margins)」 .

邊距塌陷(Collapsing margins)

在CSS中,兩個或多個框(可能是也可能不是兄弟)的相鄰邊距可以合并形成一個邊距,稱為邊距塌陷。

不會發生邊距坍塌的情況

  • 根節點元素
  • 水準邊距(

    Horizontal margins

    )不會崩潰
  • 「如果具有間隙的元素的頂部和底部相鄰,他會與後續同級的元素邊距一起坍塌,但是不會與父元素底部的一起坍塌(If the top and bottom margins of an element with clearance are adjoining, its margins collapse with the adjoining margins of following siblings but that resulting margin does not collapse with the bottom margin of the parent block.)」
  • 父子元素,父元素有非0的

    min-height

    且有

    auto

    height

    ,父子元素都含有

    margin-bottom

    ,此時

    margin-bottom

    不會發生邊距坍塌。
  • 在不同BFC(塊級格式上下文)

對于以上,可能對于「情況3」和「情況4」會比較疑惑,是以舉例子如下。

case3

在 Firefox 和 IE 下的效果(谷歌失效,原因可能和谷歌浏覽器實作有關,暫未深追。)

button是塊級元素嗎_[重拾CSS]一道面試題來看僞元素、包含塊和高度坍塌

image-20200519203941769

可以看到如果在在沒有

clearance

的情況下,父元素底部是會随着子元素一起坍塌的,但是如果中間有

clearance

的情況下,父元素的底部則不會坍塌。

case4

效果:

button是塊級元素嗎_[重拾CSS]一道面試題來看僞元素、包含塊和高度坍塌

image-20200518001513036

可以看到這種情況下,父子元素下邊距并不會發生邊距坍塌。

會發生邊距坍塌

發生邊距坍塌需要滿足2個前提

1.是 block 盒子模型,在同一個 BFC。

2.兩個元素之間沒有行内元素,沒有 clearance  ,沒有 padding,沒有border。

然後以下幾種情況會發生邊距坍塌。

  • 盒子的上邊距和第一個流入子元素的上邊距
  • 盒子的下邊距和同級後一個流入元素的上邊距
  • 如果父元素高度為“auto”,最後一個流入子元素的底部距和其父元素的底部距
  • 某個元素沒有建立新的 BFC,并且 min-height 和 height 都為 0,同時含有 margin-top 和 margin-bottom.
  • 「如果'min-height'屬性為零,并且框沒有頂部或底部邊框,也沒有頂部或底部填充,并且框的'height'為0或'auto',并且框不包含邊距,則框自身的邊距會折疊 行框,其所有流入子頁邊距(如果有的話)都會崩潰。」

補充: 如果'min-height'屬性為零,并且框沒有頂部或底部border,也沒有頂部或底部padding,并且元素的'height'為0或'auto',并且沒有行内元素,則元素自身的所有邊距坍塌,包括其所有流入子元素的邊距(如果有的話)都會坍塌。

「這裡有幾個問題要解釋一下 1.什麼是流入子元素,2. 是什麼 clearance」

1.流入元素

流入元素需要用的反向來進行介紹,有流入元素,就有流出元素,以下情況為流出元素。

  • floated items。浮動的元素
  • items with

    position: absolute

    (including

    position: fixed

    which acts in the same way)。通過設定position屬性為absolute或者fixed的元素
  • the root element (

    html

    )根元素

除了以上情況的元素,叫做流入元素。

button是塊級元素嗎_[重拾CSS]一道面試題來看僞元素、包含塊和高度坍塌

image-20200519013902840

2.clearance

當某個元素有clear 非 none 值 并且盒子實際向下移動時,它叫做 clearance。

case1
button是塊級元素嗎_[重拾CSS]一道面試題來看僞元素、包含塊和高度坍塌

image-20200519001450483

case2
button是塊級元素嗎_[重拾CSS]一道面試題來看僞元素、包含塊和高度坍塌

image-20200519001526280

case3
button是塊級元素嗎_[重拾CSS]一道面試題來看僞元素、包含塊和高度坍塌

image-20200519001635407

case4
button是塊級元素嗎_[重拾CSS]一道面試題來看僞元素、包含塊和高度坍塌

image-20200519001704179

邊距塌陷如何解決

通用型

1.改變盒子模型(非 block 模型)

2.建立新的 BFC

限制型

檢視剛才不會發生高度坍塌的情況

邊距塌陷如何計算

1.當兩個或更多邊距坍塌時,當邊距全為正數的時候,結果頁邊距寬度是邊距塌陷寬度的最大值。

2.當邊距全為負數的時候,取最小值。

3.在存在負邊距的情況下,從正邊距的最大值中減去負邊距的絕對值的最大值。 (-13px 8px 100px疊在一起,則邊距塌陷的值為 100px - 13px = 87px)

如果轉為算法就是以下代碼

小結

通過上面對邊距坍塌的了解,我們可以很快得出,我們的自适應正方形中的例子,子元素的

margin-top

和 父元素的

margin-top

發生了坍塌,是以可以建立一個 BFC 來消除這個問題。而

overflow:hidden

就是會形成一個 新的 BFC 。BFC詳見 https://developer.mozilla.org/zh-CN/docs/Web/Guide/CSS/Block_formatting_context

總結

通過上面的解析,我們終于把這一道小小的面試題,進行了全方位的剖析。每一個問題都對應着一個知識塊。

  • ::after

    僞元素有什麼特殊的魔法嗎?  ->  僞元素(Pseudo elements)
  • margin-top:100%

     為什麼能夠自适應寬度?  -> 包含塊 (Containing blocks)
  • overflow:hidden

    在這裡是什麼作用?  -> 邊距塌陷(Collapsing margins)

想不到小小的面試題,居然可以牽扯出這麼多的知識,是以我們在面對一些面試題的時候,例如實作一個自适應的正方形布局,别單單看有幾種方式能夠實作,解決方法永遠會随着時間的推進,變得越來越多,那我們能做的就是以不變應萬變(當然規範也是相對的,也可能會變,隻是機率低)去了解剖析這些方法背後的用到的知識。

相信如果你把以上搞懂了,面試官對你深層次的靈魂追問,你也能對答如流了。注意本文的一些專有名詞,我都用英文多次标注,這也許未來會對你有所幫助。

穩住,我們能赢!嘻嘻嘻,最後,如果你對題目的了解一時間比較迷茫,歡迎加群提問,本文也是基于群友的問題,展開了一系列的講解。

參考連結

https://stackoverflow.com/questions/21685648/what-exactly-is-clearance-in-css

https://www.w3.org/TR/css-display-3/#in-flow

https://stackoverflow.com/questions/25350805/margin-collapse-and-clearance

https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Flow_Layout/%E5%9C%A8Flow%E4%B8%AD%E5%92%8CFlow%E4%B9%8B%E5%A4%96

關注

歡迎關注公衆号 「「秋風的筆記」」,主要記錄日常中覺得有意思的工具以及分享開發實踐,保持深度和專注度。

button是塊級元素嗎_[重拾CSS]一道面試題來看僞元素、包含塊和高度坍塌