CSS魔法堂:說說Float那個被埋沒的志向

前言

 定位系統中第一難理解就是Normal flow,而第二就非Float莫屬了,而Float難理解的緣由有倆,1. 一開頭咱們就用錯了;2. 它跟Normal flow靠得太近了。本文嘗試理清Float的特性和行爲特徵,如有紕漏望各位指正。css

被埋沒的志向——文字環繞

 回憶一下咱們通常何時會想用浮動呢?是多列布局仍是多列布局呢:)?其實它嚮往的倒是這個

 它想幹的就是這個——文字環繞,並且CSS2中除了浮動外沒有其餘屬性可實現上述的效果。
 那到底如何理解它的實現原理呢?下面咱們採起分步剖析的方式來深刻探討吧!html

切斷關聯看Float

'float'
Value: left | right | none | inherit
Initial: none
Applies to: all
Inherited: no
 當設置float:left後,元素對應的margin left edge會盡量向所屬的containing block的左邊框靠近,若同一行中存在位於左側的元素設置了float:left,則即會盡量向該兄弟元素的margin right edge靠近.web

<div style="background:#06F;width:200px;height:100px;position:relative;left:20px;">
  <div style="background:#1F0;width:50px;height:50px;float:right;"></div>
  <div style="background:#F60;width:50px;height:50px;float:right;"></div>
</div>


(因爲float:left突出不了效果,所以採用float:right做例子。其中藍色區域就是containing block範圍,綠和紅色塊採用向右浮動)
 當設置浮動後,display:inline的實際值將被改寫爲display:block,所以不要再爲display:inline;height:100px;line-height:0;float:left致使盒子content height爲100px感到驚訝了。也不要爲即便剩餘空間不足以存放整個display:inline;float:left盒子,致使整個盒子下移到下一行排版而驚訝了.(若爲Normal flow則會根據white-spacing、word-wrap和word-break決定盒子內部份內容換行,而不是整個盒子換行)簡單來講並非float:left讓盒子具備不爲五斗米折腰的氣質,而是display:block的功勞,又因爲浮動的盒子會以水平方向排版,所以咱們能夠以display:inline-block來理解浮動定位的水平排版和換行行爲。wordpress

<div style="background:#06F;width:200px;height:100px;">
  <span style="background:yellow;width:100px;height:50px;float:left;">I'm span</span>
  <span style="background:#F01;width:110px;height:50px;float:left;">I'm span too</span>
</div>


 當設置浮動後,雖然display的實際值爲block但就width:auto而言,我認爲display更像是採用inline-block,寬度由子元素決定。這就是包裹性了!
(float:right同理,只是方向不一樣而已)
注意:在僅考慮浮動元素自己的前提下,float:left的效果與display:inline-block而父容器direction:ltr的效果是同樣的,不一樣的是浮動元素不歸入父容器高度的計算當中佈局

<div style="border:solid 1px #06F;">
  <span style="background:#F01;float:left;">float:left</span>
</div>
<br clear="both"/><br/>
<div style="border:solid 1px #06F;">
  <span style="background:#F01;display:inline-block;">float:none</span>
</div>

頭痛的開始——基於Normal flow看Float

 用割裂的方式理解float並不難,難就難在結合Normal flow看Float。下面咱們一塊兒來探討吧!警告,前方高能,前方高能!!網站

以Normal flow爲基礎

 不論是Absolute positioning仍是Float均以Normal flow做爲定位基礎,也就是說先假設有一個虛擬盒子以Normal flow進行定位,而後在這個基礎上添加Float的特性並影響其餘盒子的佈局。而浮動定位對於盒子自身而言僅影響其在水平方向上的定位,所以對於inline-level box而言其垂直方向上的定位並無發生變化,而對於block-level box而言因Collapsing margins的失效有可能會引發垂直方向上的移動。
編碼

<div style="background:#0f6;width:200px;height:50px;margin-bottom:50px;"></div>
<div style="background:#f06;width:200px;height:50px;margin:50px 0;"></div>
<div style="background:#06F;width:200px;height:50px;margin-top:50px;float:left;">float:left</div>

值得注意的是,浮動定位的虛擬盒子其實是不佔空間的。所以纔有後續的浮動閉合和清除浮動的事。spa

壓榨line box


 文字環繞很明顯就是活生生地把文字向兩邊擠,爲"大哥"留下個位置,並且小弟們不要走太遠,必須時刻擁護着大哥。那大哥是如何圈住小弟們的呢?那得藉助外力——line box。文字是以字形(glyph)的形式渲染,和它同一行的inline-level boxes均位於同一個line box中。而line box可謂是夾在containing block和浮動盒子之間勉強生存。
.net

<div style="overflow:hidden;line-height:1.5;background:#06F;">
<img src="john.png" style="float:left;margin:10px"/>
These days it takes a diverse and complex collection of components to power a web browser. <img src="john.png" style="float:right;margin:10px"/>It’s fair to think of all those parts coming together as a single piece of machinery, and we often talk about our web platform as an 「engine」.
</div>

 若line box的寬度不足以容納glyph和inline-level boxes時,會在下方產生N個新的line boxes並在必要時拆分inline-level boxes,而後將glyph和inline-level boxes分佈到各行的line boxes當中。設計

腳踩block-level box

 相對line box,block-level box就顯得不屈不撓了。width:auto時其寬度始終保持佔滿containing block寬度的態度。但位於同一個stacking context中的浮動定位的盒子雖然和常規流中的盒子擁有相同的z-index(都是auto),但浮動定位的盒子擁有額外的優先級,致使它總位於常規流中的盒子之上。(關於分層顯示的內容可參考《CSS魔法堂:你真的理解z-index嗎?》

<div style="float:left;border:solid 1px red;width:100px;height:50px;">float:left</div>
<div style="background:#06f;width:200px;height:100px;"></div>

經過建立BFC翻身作主人

 一樣是盒子,爲啥你就能夠在我上面呢?你有Float罩着,我也找弄個新的BFC來跟你抗衡。咱們知道經過float:left|rightposition:absolute|fixeddisplay:inline-block|table-cell|table|table-captionoverflow:auto|scroll|hidden都可讓盒子產生新的BFC。而產生BFC的盒子間天生排斥彼此。(但可經過後天的努力position:relative讓他們又互有交集^_^)
 那如今的問題是採用Normal flow定位模式的會產生新的BFC的盒子究竟是緊跟在Float定位盒子的後面,仍是另起一行呢?答案是二者都有可能,具體看剩餘的寬度是否足以容納該盒子。其實就是如同設置父容器產生BFC,而該盒子採用Float定位模式。不信,你看

<div style="float:left;border:solid 1px red;width:100px;height:50px;">float:left</div>
<div style="background:#06f;width:200px;height:100px;overflow:hidden;"></div>

是"浮動閉合"仍是"清除浮動"?

 我想各位都看過各類版本的clearfix實現,而最簡單粗暴的方式就是添加一個<div style="clear:both"></div>來清除浮動。我還聽過另外一個名稱——"浮動閉合",那到底二者有什麼區別呢?在做區分以前咱們先要明確問題的自己。
 對於height:auto的容器而言,咱們但願它能剛好包裹着全部子元素,但不幸的是採用浮動定位模式的子元素將不歸入父容器的高度計算當中,那就會出現子元素戳穿父容器的風險。
 從以前的內容咱們瞭解到文字和inline-level boxes會環繞Float定位的盒子,而block-level box則被它踩在腳下。但如今但願後續盒子再也不與Float定位的盒子有任何瓜葛。
 面對這兩種需求,咱們分別得出"浮動閉合"和"清除浮動"兩套方案。

浮動閉合

 就是讓height:auto的父容器包裹全部子元素,包括Float定位的子元素。方式很簡單,就是好讓父容器產生BFC。

清除浮動

 就是爲浮動影響的範圍劃邊界。方式也很簡單,就是以一個clear:left|right|both的盒子做爲邊界便可,其實就是引入空隙(clearance)。
 首先clear屬性僅對block-level box有效clear:left表示盒子的margin-left-edge不與浮動盒子接觸,而clear:right表示盒子的margin-right-edage不與浮動盒子接觸,clear:both天然是左右兩條margin-edge均不與浮動盒子接觸啦。有點虛,直接看療效吧!

<div style="float:left;width:200px;height:50px;background:#06F;">float:left</div>
<div style="clear:left;width:200px;height:50px;background:#F60;">clear:left</div>
<div style="float:right;width:200px;height:50px;background:#06F;">float:right</div>
<div style="clear:right;width:200px;height:50px;background:#F60;">clear:right</div>

 簡單地說就是float:leftclear:left來清除,float:rightclear:right來清除。而咱們會發現一個怪異的現象,那就是設置clear:left|right|both的盒子的border top edge緊接着Float定位盒子的margin bottom edge,其實這是clearance來做祟。當設置clear:left|right|both的盒子A的border top edge與Float定位盒子B的margin box重疊時,那麼就會在A的margin box和border top edge之間引入clearance,剛好讓A的的border top edge剛好不與B的margin bottom edge重疊。

<div style="margin-bottom:50px;background:#06F;height:100px;width:200px;float:left;"></div>
<div style="margin-top:50px;border: solid 10px red;height:50px;width:200px;clear:left;"></div>

.clearfix方案

 不論是浮動閉合也好,清除浮動也罷,咱們的目的每每是二者結合——Float定位的範圍與Normal flow定位的範圍分明,且採用Normal flow的父容器包裹全部子元素。那麼可歸結爲Normal flow的父容器包裹全部子元素。所以獲得以下的HTML Markup

<div class="container clearfix">
   <!-- Float定位的範圍 -->
</div>
<!-- Normal flow定位的範圍 -->

而具體的方案以下:
方案1

.clearfix::after{
  content: ".";
  display: block;
  clear: both;
  line-height: 0;
  visibility: hidden;
}
.clearfix{
  *zoom: 1; /*for IE5.5/6/7*/
}

僞元素after表示建立一個display:block,innerText是content屬性值的元素做爲該元素的最後一個子元素。注意content屬性值不能爲空白,不然沒法清除浮動。
方案2

.clearfix::after{
  content: "\u200B"; /*經過零寬空白字符,省略visibility屬性*/
  display: block;
  clear: both;
  line-height: 0;
}
.clearfix{
  *zoom: 1; /*for IE5.5/6/7*/
}

注意:若頁面不是採用UTF-8編碼方式,那麼\u200B表示的將不是零寬空白字符,從而致使方案2出問題。
方案3
由Nicolas Gallagher大溼提出的

.clearfix::before, .clearfix::after{
  content: "";
  display:table;
}
.clearfix::after{
  clear: both;
}
.clearfix{
  *zoom: 1; /*for IE5.5/6/7*/
}

這裏有2個奇妙的地方:

  1. 經過display:table讓即便content爲空白時,也能獨佔據一行,且高度爲0;(原理是display:table會生成一個block-level box包裹着僞元素after)
  2. 經過僞元素before消除父容器margin-top與第一個Normal flow的子元素的margin-top產生margin collapsing效果。

浮動真的是定位模式的一員嗎?

 咱們能夠經過position屬性來設置Normal flow或Absoluting positioning,但卻要經過float屬性來設置Float,這讓我一度懷疑Float究竟是不是定位模式的一員呢?
 我是這樣理解的,Normal flow(包括Relative positioning)與Absoluting positioning是非我即你的關係,而Float和Relative positioning則是可疊加影響定位效果的關係,顯然必須另設一個屬性來設置更恰當。

期待更美的文字環繞

 有沒有發現經過float:left|right咱們僅能獲得要麼圖片靠左要麼圖片靠右的文字環繞效果,那若是咱們但願獲得以下的四周環繞的效果呢?

 雖然已有案例是經過absolute positioning模擬出相似的效果,但佈局排版固定致使沒法適應大部分場景。若是有個float:both屬性值那該多好啊!另外你們是否以爲如下的環繞效果更有藝術範呢?

 據說經過CSS3的shapes特性能夠實現四周環繞和上面非四四方方的環繞效果,往後好好研究研究!
2016/04/19補充-參考《CSS網站佈局實錄-基於Web標準的網站設計指南(第2版)》的5.2.2 不規則文字環繞

<style type="text/css">
.article{
  font-size: 14px;
  line-height: 1.5;
  text-align: justify;
}
.figure{
  position: absolute;
  z-index: -1;
}
.figure-shape{
  margin: 0;
  padding: 0;
}
.figure-shape li{
  list-style-type:none;
  height: 1.5em;

  float: left;
  clear: left;
}
.figure-shape li:nth-child(1){
  width: 150px;
}
.figure-shape li:nth-child(2){
  width: 180px;
}
.figure-shape li:nth-child(3){
  width: 180px;
}
.figure-shape li:nth-child(4){
  width: 160px;
}
.figure-shape li:nth-child(5){
  width: 148px;
}
.figure-shape li:nth-child(6){
  width: 150px;
}
.figure-shape li:nth-child(7){
  width: 148px;
}
.figure-shape li:nth-child(8){
  width: 144px;
}
.figure-shape li:nth-child(9){
  width: 136px;
}
</style>
<div class="article">
<img src="./beyonce.jpg" class="figure"/>
<ul class="figure-shape"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul>
初中時候語文老師說我會是個寫做天才,由於我寫的東西足夠真實,取材身邊,造句簡單,用語文書墊桌腳的同時翻爛了韓寒的1988,那時督促我已經成爲她的習慣。時至今日再次碰見語文老師時候我慚愧的告訴她我已經不寫文了,也沒有像她說的那樣成爲一個天才,我只能微微一笑告訴她我至少還沒停下筆。
</div>

總結

重構了幾回總算寫完了,想寫得清楚而又不哆嗦真心不易,繼續努力:)
尊重原創,轉載請註明來自:http://www.cnblogs.com/fsjohnhuang/p/5375753.html^_^肥子John

感謝

KB011: 浮動(Floats)
KB009: CSS 定位體系概述
CS001: 清理浮動的幾種方法以及對應規範說明
CSS float浮動的深刻研究、詳解及拓展(一)
CSS float浮動的深刻研究、詳解及拓展(二)
https://www.w3.org/TR/CSS2/visuren.html#flow-control
CS001: 清理浮動的幾種方法以及對應規範說明
Faking ‘float: center’ with Pseudo Elements
說說標準——CSS核心可視化格式模型(visual formatting model)之十:控制緊接浮動的排列-clear 特性
那些年咱們一塊兒清除過的浮動

相關文章
相關標籤/搜索