引子:給大家出一個小小的考題,如何使用css來實現類似下面的在指定區域內,內容自適應的垂直居中。
在很久很久以前,有一個并不堅挺的方式來實現。
display:table-cell屬性指讓標簽元素以表格單元格的形式呈現,類似于td標簽。
其實,內心里討厭這個方式的原因有很多,
比如,高級瀏覽器都是支持此屬性的,但是低級瀏覽器就只能對你說sorry了。對于需要使用各種并不精準的其他屬性,如font-size來“輔助 治療”,在不同場合容易出現一些預期之外的bug。如果一個問題通過新特性和老特性都可以解決,我想所有人都會選擇通用,并且穩定性好的那一種。
比如,經歷過table年代的攻城師們想起table沾邊的就讓人覺得一點也不高大上,深深的產生厭惡感。
那么,該如何實現更合理呢?………..請耐心讀完這篇長篇大論的博客。
接下來進入正題。
說一個定義,他是一種抽象的玩意,他叫做IFC : inline formating context(行內格式化上下文)
與IFC之對應的是BFC : block formating context(塊級格式化上下文),BFC在網頁布局中的應用場景更是極其豐富,了解它會了解很多bug出現的原因,以及自行找到解決辦法。但是此篇博 客不是討論他的,所以即使說到了他有了一肚子的傾訴欲也得留到下次再說。
IFC跟BFC一樣,它不是一個元素,不是一種屬性,而是一種環境,一種上下文,一種布局特性。
引用w3c(http://www.w3.org/TR/CSS2/visuren.html#inline-formatting)
In an inline formatting context, boxes are laid out horizontally, one after the other, beginning at the top of a containing block. Horizontal margins, borders, and padding are respected between these boxes. The boxes may be aligned vertically in different ways: their bottoms or tops may be aligned, or the baselines of text within them may be aligned. The rectangular area that contains the boxes that form a line is called a line box.
The width of a line box is determined by a containing block and the presence of floats. The height of a line box is determined by the rules given in the section on line height calculations.
A line box is always tall enough for all of the boxes it contains. However, it may be taller than the tallest box it contains (if, for example, boxes are aligned so that baselines line up). When the height of a box B is less than the height of the line box containing it, the vertical alignment of B within the line box is determined by the ‘vertical-align’ property. When several inline-level boxes cannot fit horizontally within a single line box, they are distributed among two or more vertically-stacked line boxes. Thus, a paragraph is a vertical stack of line boxes. Line boxes are stacked with no vertical separation (except as specified elsewhere) and they never overlap.
In general, the left edge of a line box touches the left edge of its containing block and the right edge touches the right edge of its containing block. However, floating boxes may come between the containing block edge and the line box edge. Thus, although line boxes in the same inline formatting context generally have the same width (that of the containing block), they may vary in width if available horizontal space is reduced due to floats. Line boxes in the same inline formatting context generally vary in height (e.g., one line might contain a tall image while the others contain only text).
When the total width of the inline-level boxes on a line is less than the width of the line box containing them, their horizontal distribution within the line box is determined by the ‘text-align’ property. If that property has the value ‘justify’, the user agent may stretch spaces and words in inline boxes (but not inline-table and inline-block boxes) as well.
When an inline box exceeds the width of a line box, it is split into several boxes and these boxes are distributed across several line boxes. If an inline box cannot be split (e.g., if the inline box contains a single character, or language specific word breaking rules disallow a break within the inline box, or if the inline box is affected by a white-space value of nowrap or pre), then the inline box overflows the line box.
When an inline box is split, margins, borders, and padding have no visual effect where the split occurs (or at any split, when there are several).
Inline boxes may also be split into several boxes within the same line box due to bidirectional text processing.
Line boxes are created as needed to hold inline-level content within an inline formatting context. Line boxes that contain no text, no preserved white space, no inline elements with non-zero margins, padding, or borders, and no other in-flow content (such as images, inline blocks or inline tables), and do not end with a preserved newline must be treated as zero-height line boxes for the purposes of determining the positions of any elements inside of them, and must be treated as not existing for any other purpose.
一大堆英文看的鴨梨山大,經過個人的理解總結,翻譯和理解如下(原諒我的東北話和長篇大論):
在IFC中,內聯元素在水平方向上一個接一個的排布,其中,容器之間水平方向上的margin,padding,border方向上是好使的。他們垂直方向上有很多種對其方式,比如居底部或頂端對齊,或者基線對齊。他們對齊完了之后形成的這個四方塊兒區域,叫做一個line box(行框)。
一個line box的寬度由包含它的元素的寬度和包含它的元素里面有沒有float元素來決定,而高度由內部元素中實際高度最高的元素而計算出來。
//其實這句話解釋了為什么內聯元素是不能設置垂直方向的padding,margin等,因為即使設置了,也不會影響line box的高度,可能會在每個瀏覽器的表現各異,但大多數不會達到預期的效果。
line box的高度是足夠高來包含他內部的容器們的,也可能比它包含的容器們都高(比如在基線對齊的時候),當他包含的內部容器的高度小于line box的高度的時候,內部容器的垂直位置由自己的vertical這個屬性來確定。當內部的容器盒子太多了一個line box裝不下來,他們折行之后會變成兩個或者多個line box, line box們相互之間垂直方向不能分離,不能重疊。
一般來說,line box的左邊緣挨著包含它的元素的左邊緣,并且右邊緣挨著包含它的元素的右邊緣,浮動元素會在包含他們的元素的 邊緣和line box的邊緣之間,所以,雖然在同一個IFC下的line box們通常擁有相同的寬度(就是包含他們的容器的寬度),但是也會因為浮動元素的搗亂,導致line box們的可用寬度產生了變化不一樣了。在同一個Ifc下的line box們的高度也會不一樣(比如說,一個line box里有個比較大的image,他就高了)。
個人補充:
line box 圖(趁機秀照片):
如果一個line box 里的內聯元素們的寬度總和小于這個line box的寬度,那么他們在這個line box里的水平方向的排布方式由 text-align這個屬性來決定,如果這個屬性被設置成了“justify”,可以使這些盒子在剩余空間內拉伸(除了inline-table 和 inline-block的元素)。//實現類似兩端對齊的效果,但不是所有瀏覽器都支持。
當內聯元素的寬度超過了line box的寬度,那么它會折行分裂成了幾個line box,如果這個元素里面的內容不可以折行,例如只有一個字,或者white-space設置了nowrap/pre。那么內聯元素會溢出line box。
當一個內聯元素分裂時,分裂處的 margins, borders 和 padding不會有任何視覺效果(或者其他任何分裂,只要是有多個line box)。
line box 的生存條件是在IFC中并且包含inline-level元素,如果line box里沒有文本,空白,換行符,內聯元素,也沒有其他的存在IFC環境中的元素,(如inline-block,inline- table,images等),將會被視為零高度,也將會被視為沒有意義。
補充:在IFC的環境中,是不能存在block-level元素的,如果將block-level元素插入到IFC中,那么此IFC將會被破壞掉, 而block-level元素前的元素和block-level元素后的元素將會各自自動產生一個匿名容器其包圍,這個匿名的容器內部環境將是一個新的 IFC。
例1:
P是一個塊元素,它包含了5個內聯元素,其中,有三個是匿名的。
- 匿名: “Several”
- EM: “emphasized words”
- 匿名: “appear”
- STRONG: “in this”
- 匿名: “sentence, dear.”
這段代碼里,在line box里,有5個inline的元素,P建立了包含line box的容器箱。
如果這個P的寬度足夠,將會產生唯一的一個line box。如下圖:
如果P的寬度不夠,將會被分割成多個line box。如下圖:
例2:
1.margin,在emphasized前和word之后水平方向上起了作用。分割處無作用,垂直方向無作用。
2.padding,在emphasized前和word之后水平方向上起了作用。分割處無作用,垂直方向無作用。
3.border,看那個虛線的表現形式。
經過了上面的闡述,我們回到博客開使的問題上。實現未知尺寸的文本/元素/圖片 在某個元素內垂直居中。
上面曾經說過:
在一個line box中,當他包含的內部容器的高度小于line box的高度的時候,內部容器的垂直位置由自己的vertical這個屬性來確定。
那么,我們設想一下,如果手動創建一個IFC的環境,讓line box的高度是包含塊的高度的100%,讓line box內部的元素使用vertical-align:middle,就可以實現垂直居中。
一個line box的高度由內部元素中實際高度最高的元素而計算出來。
所以,我們在line box中插入一個高度100%的inline-block元素。則會把整個line box撐高直到包含塊的100%.
概念圖:
其中,高級瀏覽器可以直接在外面的包含層使用:after來在內部追加創建一個偽元素,而低級瀏覽器不支持:after的寫法,則在html模板中 創建一個空的元素, 設置其高度為100%則與包含層相同,寬度為零,內容為空,則可以實現將自己內部撐大為一個line box,但不占據任何空間。那么自己內部的其他元素可以通過轉化為inline 或者 inline-block 來實現在line box中的垂直居中。表現形式就是在外面的包含層中垂直居中。
代碼如下:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>IFC應用實例</title> <meta name="copyright" content="www.ohweb.cn" /> <meta name="author" content="weinan" /> </head> <body> <style> /* comm 核心部分 */ .g-ifc-wrap:after, .g-ifc-wrap-after{content:'';height:100%;width:0;display:inline-block;*display:inline;*zoom:1;margin-left:-5px;font-size:0;vertical-align:middle;} .g-ifc-item{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;} /* test */ #test{height:300px;width:500px;background-color:#878787;border:3px solid #526f39;} #test p{background-color:#7ff30a;color:#000;font-weight:bold;} </style> <div id="test" class="g-ifc-wrap"> <p class="g-ifc-item">我想要基于外面的容器居中</p> <img class="g-ifc-item" alt="我也是" src="http://dh2.kimg.cn/v/upload/20140325/9aa6e7f4e477d9af9c463c5604070ed4.jpg?v=1395739057" /> <!--[if lte IE 7]><span class="g-ifc-wrap-after"></span>< ![endif]--> </div> </body> </html>
表現形式如下:
chrome等高級瀏覽器:
IE6:
小結 :
當我們詳細的了解IFC的原理之后,可以更有自信的應用它來做很多事情。
例如有些網站會摒棄float這種bug多多的布局方式,而全部使用IFC的環境來進行布局(當然了,很精確的時候也需要解決inline-level元素之間的間隙)。
或者實現一些如圖片文字的居中對齊,自適應高度等很多靈活的效果。
同時,當我們的網頁產生一些bug的時候,我們也可以明白為何如此并且思考好的解決方法而不是去網上找“萬金油”。這將使我們對自己的網頁有了更深的掌控性。
————————————–END
文章列表