重塑你的CSS世界觀——浮動魔鬼float

爲何要寫《重塑你的CSS世界觀》系列文章

因爲從工做到如今,個人主要工做都是寫JavaScript,幾乎沒怎麼碰CSS,一般都是別人寫好界面,而後我來開發JavaScript邏輯代碼,這致使了嚴重的偏科,CSS弱得很,因此我決定要從新學習CSS,從打好CSS2的基礎開始。最近我開始在看一本關於CSS的書,這本書叫《CSS世界》,是張鑫旭這個CSS大神寫的,質量十分有保障。css

這本書幾乎徹底顛覆了我對於CSS的認知,幾乎等於重塑個人CSS世界觀。html

子曰:學而不思則罔,思而不學則殆。

因此我決定把從這本書學習到的知識結合我看到的一些文檔來總結一下,以便之後複習使用,因此文章中的看法也有多是錯的,不追求徹底正確,而是但願可以做爲方法論來指導以後的CSS開發。若有錯誤,請諸君指正。前端

浮動的本質是什麼?

浮動的本質就是爲了實現文字環繞效果 ——《CSS世界》第六章 流的破壞和保護

熟悉張鑫旭老師的人,應該在他的博客和一些視頻教程中都有聽到上面那句話,可能和我同樣,咋一看以爲懂,而後就不覺得然就過去了,而後使用在float的時候仍是錯誤百出。瀏覽器

撇開咱們是前端開發者這個身份,讓咱們做爲一個普通用戶的角度來思考有哪些場景咱們會用到文字環繞效果。相信不少人都是使用過Word這個軟件,在咱們編輯圖文信息的時候,但願文字能夠圍繞圖片來排列,也就是文字環繞,也就是從圖1變爲圖2:svg


<center>圖1</center>佈局


<center>圖2</center>學習

普通用戶視角,作到文字環繞的的步驟

仍是同樣,讓咱們做爲一個普通用戶來思考如何實現這一效果。爲了方便,我在這裏定義一些詞彙,將文本、段落稱之爲跟隨內容,將圖片等須要被環繞的稱之爲目標元素
首先而後咱們將這二者簡化一下,分紅兩個區塊,如圖:spa

而所謂的環繞,那就是把兩個區塊重疊在一塊兒,目標元素懸浮於重疊區上面,並且重疊區不能有跟隨內容的內容,如圖:翻譯

因此要使得跟隨內容可以環繞目標元素咱們須要作到如下步驟:設計

  1. 使跟隨內容的頂部可以上升到與目標元素頂部同一水平線上;
  2. 跟隨內容目標元素的重疊區域不能有文字,不重疊區域按照原來的排版方式。

如何浮動?

脫離文檔流

浮動最初是爲了實現文字環繞效果,通過上面的討論咱們意識到了須要讓浮動元素和跟隨元素的頂部上升到同一水平線上。而在文檔流中,若是浮動元素和跟隨元素都是Div元素,它們兩在默認狀況下都將佔據一行。因此咱們要讓浮動元素脫離文檔流,這個時候跟隨元素才能根據文檔流的要求進行上移,從而達到視覺上的重疊效果。

而因爲浮動元素脫離了文檔流,若是父元素沒有指定高度或者其餘元素撐起,也就出現了所謂的浮動元素的父元素高度塌陷。

肯定「包含塊」,在「當前行」進行浮動

在進行原理分析前,讓咱們先來看一段代碼,並想象它的渲染結果

<style>
    .box {
        border:1px solid red; 
        width: 200px;
        margin-left:50px;
    }
    
    .text {
        background-color:gray;
        color:white;
    }
    
    .float {
        float:left;
        color:blue;
    }
    
</style>

<div class='box'>
    我就是
    <span class='text'>一段文本<em class='float'>浮動</em></span>
</div>

此時,你腦海裏想象的結果可能以下圖:

但實際運行效果倒是這樣的:

爲何會出現這種狀況呢?讓咱們從CSS的標準中找尋答案。W3C的CSS標準文檔關於浮動的描述中有這樣一段話:

A floated box is shifted to the left or right until its outer edge touches the containing block edge or the outer edge of another float

翻譯成中文就是:

一個浮動盒會向左或向右移動,直到其外邊界捱到 包含塊邊界或者另外一個浮動盒的外邊界。

OK,先撇開有多個浮動元素的狀況,讓咱們只考慮一個浮動元素的狀況。注意到上述引用文本中的高亮詞沒有?包含塊(containing block),沒錯,之因此剛纔咱們對代碼運行的渲染效果的想象與實際瀏覽器運行的效果產生誤差,就是由於咱們沒有意識到包含塊(containing block),或者識別錯了浮動元素的包含塊,而這個包含塊就是元素浮動的參考位置!

什麼是包含塊?若是判斷一個元素的包含塊?

在W3C的CSS標準文檔中關於包含塊的描述中有這樣一段話:

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

翻譯成中文就是:

元素(生成的)盒的位置和大小有時是根據一個特定矩形計算的,叫作該元素的包含塊(containing block)

關於如何肯定一個元素的包含塊,標準也給出了定義,因爲英文的較長,閱讀起來比較吃力,在此我放一段中文的定義:

    1. 根元素所在的包含塊是一個被稱爲初始包含塊的矩形。對於連續媒體,尺寸取自視口的尺寸,而且被固定在畫布開始的位置;對於分頁媒體就是頁區(page area)。初始包含塊的'direction'屬性與根元素的相同
    1. 對於其它元素,若是該元素的position是'relative'或者'static',包含塊由其最近的塊容器祖先盒的內容邊界(the content edge )造成
    1. 若是元素具備'position: fixed',包含塊由連續媒體的視口或者分頁媒體的頁區創建
    1. 若是元素具備'position: absolute',包含塊由最近的'position'爲'absolute','relative'或者'fixed'的祖先創建,按照以下方式:
      1. 若是該祖先是一個行內元素,包含塊就是環繞着爲該元素生成的第一個和最後一個行內盒的內邊距框的邊界框(bounding box)。在CSS 2.1中,若是該行內元素被跨行分割了,那麼包含塊是未定義的
      1. 不然,包含塊由該祖先的內邊距邊界造成

          若是沒有這樣的祖先,包含塊就是初始包含塊

根據上面關於包含塊的定義,咱們能夠發現.float元素符合第2條,那麼它的包容塊就是.box這個div內容邊界,注意是內容邊界(the content edge ),而不是整個盒子,因此不包含padding、border、margin。

讓咱們先來看一張關於盒模型圖:

W3C的CSS規範關於content edge的定義是:

The content edge surrounds the rectangle given by the width and height of the box, which often depend on the element's rendered content The four content edges define the box's content box.

翻譯成中文就是:

內容邊界環繞着盒的width和height指出的矩形,一般取決於元素的呈現(rendered)內容。4條內容邊界定義了盒的內容框(content box)

因此若是咱們給.box類添加一個padding爲10px的話,

.box {
    padding: 10px;
}

浮動元素因爲是貼着包容塊在浮動,而此時的包容塊僅僅是.box元素的內容區,不包含padding,因此就出現了,浮動元素與.box元素盒子之間的間距,效果以下:

OK,那如何經過只調整CSS的方式就達到咱們一開始腦海裏想象的渲染效果呢?答案就是讓.textspan元素成爲.float元素的包含塊就好了,讓inline level element變成block level element,只需添加一句dispaly:inline-blockdispaly:block便可,這裏咱們還想保持.text元素的內聯性,因此咱們添加:

.text {
    display:inline-block;
}

此時的效果就是咱們一開始想要的了

在「當前行」浮動

W3C的CSS標準規範中關於浮動的定義,一開頭就有這麼一句:

A float is a box that is shifted to the left or right on the current line.

翻譯成中文就是:

浮動(盒)就是一個在 當前行向左或向右移動的盒。

你們注意高亮詞當前行(the current line),必定要理解這個當前行,若是說包含塊(containing block)決定了浮動元素浮動的範圍的話,那這個當前行(the current line)就決定了在浮動範圍的哪一個垂直位置進行浮動。

一樣,咱們經過一個小場景來了解這個當前行的重要性。

想象一下,你接到了一個任務,要求實現一個三列布局,不管左右兩列的寬度怎麼變化,中間列的寬度都要自適應,效果大概以下圖(爲了突出效果,給三列都添加了背景顏色):

聰明的你可能已經想到了辦法,讓左右兩列浮動,中間列的左右margin值爲auto,這樣就能夠實現中間列自適應了。沒錯你以爲十分機智,而後按照設計圖的視覺你從左到右寫了三個div

<style>
    .container {
        width: 600px;
        color:white;
        text-align:center;
        font-size:30px;
    }
    .left {
        float:left;
        background-color:yellow;
        width:200px;
        height:200px;
    }
    .right {
        float:right;
        background-color:red;
        width:200px;
        height:200px;
    }    
    
    .middle {
        height: 200px;
        margin: 0 auto;
        background-color:blue;
    }
    
</style>
<div class='container'>
    <div class='left'>Left</div>
    <div class='middle'>Middle</div>
    <div class='right'>Right</div>
</div>

然而你運行代碼,你看到的效果倒是這樣的:

而後你的心情奔潰了:

遇事不慌,首先讓咱們分析一下,浮動元素.right的包含塊是什麼?沒錯,就是.container內容邊界(content edge),因此雖然有點差錯,但好歹還在裏面浮動着。
那接下來就是肯定當前行(current line)了。

讓咱們還原到最開始的時候的佈局,去除.left.right的浮動屬性,此時會有"三行"

首先咱們讓.left浮動起來,在.container的內容邊界範圍中、「第一行」中向左浮動,因爲其脫離了文檔流,.middle開始上移到「第一行」。
此時因爲.middle的上移,.right元素也向上移動到了「第二行」,而後讓它浮動,此時.right的浮動範圍是.container的內容邊界範圍中,而浮動的當前行是「第二行」,因此就出現了.right元素掉下去浮動的效果了,由於它的「當前行」就是「第二行」,它其實沒有掉下去過!

那怎麼樣才能解決,很簡單,讓.left.right「同一行」不就行了。什麼意思呢?

.left浮動的當前行是「第一行」,而後因爲脫離了文檔流,後續的元素會往上移動一行,那咱們讓.right緊跟在.left以後不就好了,.left浮動後,.right就會到「第一行」,此時浮動不就到了「第一行」的右邊了麼。而後因爲.right也脫離文檔流,.middle也上移到了「第一行」,具體代碼以下:

<div class='container'>
    <div class='left'>Left</div>
    <div class='right'>Right</div>
    <div class='middle'>Middle</div>
</div>

而後就是運行以後就是咱們一開始想要的效果了:

看到這裏相信你已經理解了許多,讓咱們經過相關下面這段代碼的運行效果,來檢驗你是否真的理解了吧,提示:標題中的文字已經超出了.title的寬度了

<style>
    .title {
        width:160px;
    }
    
    .more {
        float:right
    }
</style>
<h3 class='title'>我就是一個三級標題<a href='#' class='more'>更多</a></h3>

若是你能腦補出下圖的效果,說明你已經懂了,若是還不能,請回到前文繼續閱讀。

讓咱們用上面用過的套路來分析這一現象,首先確認.more元素的包含塊爲.title元素的內容邊界,而後因爲內容過長,寬度不夠,此時就變爲了兩行顯示,而.more元素就在「第二行」,因此它的當前行就是「第二行」,添加浮動屬性以後,在.title元素的內容邊界範圍內的「第二行」向右浮動,就造成了咱們看到的樣子。

若是但願不管標題內容不管多長,.more元素都要固定浮動在右邊,那麼只須要將.more元素的代碼放置帶.title元素的最前方便可,也就是:

<h3 class='title'><a href='#' class='more'>更多</a>我就是一個三級標題</h3>

此時的效果就是咱們想要的了

重疊區不容許渲染「內容」,行框盒子「卡位」,實現文字環繞

這一部分實際上是浮動的本質,卻常常被咱們所遺忘,主要是由於這個文字環繞的效果主要影響的是浮動元素的跟隨元素。

因爲這一部分規範中的定義比較晦澀,因此如下內容是我在閱讀《CSS世界》結合自身的理解來闡述的,若是你但願查看規範的原文,能夠訪問這個連接

首先因爲浮動,浮動元素和跟隨元素出現了必定的重疊區,這一點你們都知道了,而要想內容環繞,則重疊區不能有內容,注意是內容,文本、svg、圖片等替換元素都被視爲「內容」,基本都是inline或line-block的元素。

而不管是inline仍是inline-block元素,都會產生「行框盒子」,在重疊區中不容許出現「內容」,也就能夠轉化成重疊區的「行框盒子」須要「貼在」浮動元素浮動方向的反方向的側邊便可,就是說若是一個元素向左浮動,則跟隨元素在重疊區的「行框盒子」須要「貼在」浮動元素的右側邊,反之亦然。

讓咱們看一段代碼:

<style>
    .container{
        width:250px;
        border:1px solid black;
    }
    
    .float {
        float:left;
        width: 100px;
        height:100px;
        border:2px solid red;
    }
    .paragraph {
        background-color:green;
        color:white;
    }
    
    .paragraph:first-line {
        background-color:blue;
    }
    
</style>
<div class='container'>
    <div class='float'></div>
    <div class='paragraph'>
        我是匿名內聯盒子的文本
        <span>,而我是內聯盒子的文本,若是文本很長,那就會出現balabalabala的效果。
        </span>
        充點字數哈兄弟。
    </div>
</div>

咱們用藍色背景高亮文本的第一行,就能夠看得出來「行框盒子」卡浮動元素側邊的效果了,而後注意,重疊區是不能渲染「內容」,背景顏色不屬於「內容」,因此咱們能夠看到綠色背景色連重疊區也鋪滿。

這時候可能有大兄弟要問,那重疊區究竟是浮動元素在上仍是跟隨元素在上啊?答案就是:浮動元素在上。
讓咱們加一段代碼驗證如下:

.float {
    background-color:yellow;
}

效果以下:

總結

當一個元素要進行浮動時,須要如下步驟:

    1. 脫離文檔流;
    1. 在「包含塊」範圍內及「當前行」上根據浮動方向進行浮動;
    1. 受浮動影響的元素在與浮動元素的重疊區中不容許渲染「內容」,受浮動影響的元素內的行框盒子進行「卡位」,實現文字環繞;
相關文章
相關標籤/搜索