破洛洛文章簡介:由於沒有找到自己認為完整的關於普通流、浮動和絕對定位的中文文章,於是鼓起勇氣決定自己來寫篇。為此大致啃掉了CSS2.1裡的 8 Box model 以及 9 Visual formatting model 。實話說,還真是看得有點頭大,呵呵~ 文檔流,其實標准裡根本就沒有這個詞。如果把文檔流直譯
由於沒有搜到任何自己認為完整的關於文檔流、浮動和絕對定位的中文文章,於是鼓起勇氣決定自己來寫篇。為此啃掉了CSS2.1裡的 8 Box model 以及 9 Visual formatting model 。實話說,還真是看得有點頭大,呵呵~
文檔流,其實標准裡根本就沒有這個詞。如果把文檔流直譯為英文就是 document flow ,但標准裡只有另一個詞,叫做普通流( normal flow ),或者稱為常規流。但似乎大家更習慣文檔流的稱呼,因為很多中文翻譯的書就是這麼來的。比如《CSS Mastery》,英文原書中至始至終都只有普通流 normal flow 這一詞,從來沒出現過文檔流 document flow 。但是中文譯本 “普通流” 和 “文檔流” 卻是交替出現的。
什麼是普通流?簡單說就是元素按照其在 HTML 中的位置順序決定排布的過程。並且這種過程遵循標准的描述。
為了從不同角度說明,我采集了一些可能冗長、具體或者晦澀的其他人給出的定義:
可見,把流( flow )理解為流程,完全說的通。普通流即是通常情況下的元素排布和定位流程。
但其實在CSS2.1RC裡,普通的本質是三種定位機制( Positioning schemes )之一,被定義為:
Normal flow. In CSS 2.1, normal flow includes block formatting of block boxes, inline formatting of inline boxes, relative positioning of block or inline boxes, and positioning of run-in boxes.
這個過程包括了塊格式化( block formatting ),行內格式化( inline formatting ),相對定位( relative positioning ),以及 run-in boxes 的定位。似乎和上面那些迥然不同,但是把這些分解開來,仍然是一致的。
另外,9.4 Normal flow下還有一段:
Boxes in the normal flow belong to a formatting context, which may be block or inline, but not both simultaneously. Block boxes participate in a block formatting context. Inline boxes participate in an inline formatting context.
這是段描述,不是定義。在普通流中的 Box(框) 屬於一種 formatting context(格式化上下文) ,類型可以是 block ,或者是 inline ,但不能同時屬於這兩者。並且, Block boxes(塊框) 在 block formatting context(塊格式化上下文) 裡格式化, Inline boxes(塊內框) 則在 inline formatting context(行內格式化上下文) 裡格式化。
我們知道,任何被渲染的元素都屬於一個 box ,並且不是 block ,就是 inline 。即使是未被任何元素包裹的文本,根據不同的情況,也會屬於匿名的 block boxes 或者 inline boxes。所以上面的描述,即是把所有的元素劃分到對應的 formatting context 裡。
組合上面的定義,並且姑且先把 formatting context 看做是一種范圍限定,那麼具體講,普通流就是這樣的過程:
說了一堆東西,其實就只是在說如何排布元素而已。那些都非常容易理解,除了一個概念—— formatting context 。
什麼是 formatting context ? context 總是解釋為上下文環境,那麼格式化上下文就應該是指格式化時的前後關系。
然而對此,標准裡沒有更多的定義和解釋。
雖然 mozilla developer center 上沒有關於 inline formatting context 的資料,但是卻有關於 block formatting context 的描述:一個 block formatting context 是web頁面可視化CSS渲染的一個部分,是一塊 block boxes 排布以及 float 元素相互作用的區域。
用自己的話簡言之,那是一個作用范圍。可以把它理解成是一個獨立的容器,並且這個容器的裡box的布局,與這個容器外的毫不相干。
下面的這些情況,都會創建一個新的 block formatting context:
雖然標准裡沒有提到根元素會創建新的 block formatting context ,但是mozilla提到了,並且這也解釋了初始的一個上下文環境的建立。
這裡有個建立( establishes )的概念,這個概念和建立容器塊( containing block )的概念類似。比如,A是B父元素,當B被渲染時其位置和大小會參照一個容器塊,這個容器塊是由其父元素A建立的。是的,有點簡單問題復雜化。雖然實質上父元素就是子元素的容器,但是過程中間卻有個建立( establishes )的概念。並且這個創建的概念被應用於其他作用范圍,包括 block formatting context 。
想想我們平常在做的事情。當一個父元素因為子元素浮動而導致高度為0的時候,也許我們會習慣的加上這樣的規則:overflow:hidden;zoom:1; 。
overflow:hidden 不正是創建了一個新的 block formatting context 嗎?那麼 zoom:1 是怎麼回事?這不得不提到 IE 私有的 hasLayout ,一個和 block formatting context 行為相仿的IE特產。對於 hasLayout ,本文不做討論。可以閱讀那篇有名的 On having layout ,中文版由 old9 翻譯過,但是鏈接似乎暫時掛掉了,所以可以看看藍色理想上轉載的版本。
這就是為什麼浮動元素總是容納浮動元素的原因——浮動元素創建了新的 block formatting context,其內部的布局不在和外部有關。比如內部的浮動清除不會再影響到外部,並且內部的浮動對於外部而言也是不可見的。這也是為什麼《精通CSS》會說 “應用值為 hidden 或 auto 的 overflow 屬性會自動地清理任何浮動元素” 的原因。同時,這也是為什麼有的時候必須用清除浮動而不是設置overflow來使父容器容納浮動元素,因為 “設置框的overflow屬性會影響它的表現”。
舉個實際當中的常見例子,比如垂直邊距疊加問題:
1 2 3 4 5 6 7 8 9 10
<html> <head></head> <body> <div id="A" style="background:gray;margin-top:20px;"> <div id="B" style="margin-top:10px;"> 我有10px m-t </div> </div> </body> </html>
如果在現代的浏覽器裡運行,那麼 B 的10px上邊距將會和父元素 A 的20px上邊距重疊起來,從而看起來就好像 B 沒有上邊距一樣。就像前面說的那樣,同一個 block formatting context 中的垂直邊界將被重疊。那麼不同 block formatting context 呢?
這種情況,如果不想加邊框( border:1px solid transparent; )解決的話,那麼就需要 A 建立一個新的 block formatting context 將 B 包裹起來,那麼 A 的上邊距和 B 的上邊距就毫不相干了。可以用浮動,也可以加 overflow ,這要看具體情況。
1 2 3 4 5 6 7 8 9 10
<html> <head></head> <body> <div id="A" style="background:gray;margin-top:20px;overflow:auto;"> <div id="B" style="margin-top:10px;"> 我有10px m-t </div> </div> </body> </html>
再一個例子,回頭看下上面過程裡這樣的一句:除非創建一個新的 block formatting context ,否則塊級元素的布局不受浮動元素的影響。這是造成元素重疊的原因。比如下面的代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13
<html> <head></head> <body> <div id="A" style="background:gray;overflow:auto;"> <div id="B" style="background:green;margin:10px 10px 0 0;float:right;"> 浮動元素m_10_10_0_0 </div> <div id="C" style="background:red;"> 普通流塊 </div> </div> </body> </html>
普通流塊 C 在容器 A 裡排布的時候,並不受浮動元素的影響,甚至當浮動元素 B 不存在。但是如果 C 創建了新的 block formatting context ,那麼,普通流塊c就會像 line box 一樣受到浮動元素存在的影響而縮小。
1 2 3 4 5 6 7 8 9 10 11 12 13
<html> <head></head> <body> <div id="A" style="background:gray;overflow:auto;"> <div id="B" style="background:green;margin:10px 10px 0 0;float:right;"> 浮動元素m_10_10_0_0 </div> <div id="C" style="background:red;overflow:auto;"> 普通流塊 </div> </div> </body> </html>
關於浮動和 block formatting context ,brunildo.org 上有一份不錯的參考。
說了這麼多了,其實概念仍然可以很簡單。普通流僅僅只是一種定位的機制。而flow本身在標准裡也可以作為一個動詞,就如同按順序一個個的拿出HTML元素然後放到頁面上一般。只是要注意一下 formatting context 的概念,特別是 block formatting context ,因為其影響更大(包括邊距重疊、浮動清除、元素重疊等)。
關於更多 block formatting context 以及例子,可以參看 CSS 101: Block Formatting Contexts 或者 Control Block Formatting Context。
寫到這裡也就差不多了,由於水平所限,如文中有不當之處,請指點下,非常感謝。謝謝閱讀
原創。原文:http://www.swordair.com/blog/2010/08/415,轉載請保留。