引子:给大家出一个小小的考题,如何使用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个内联元素,其中,有三个是匿名的。
这段代码里,在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