本文承接CSS2.1SPEC: 视觉格式化模型之width属性详解(上),继续分析CSS视觉格式化模型中width以及相关值的计算问题:
注:与上节不同,本节的demo中由于出现了float,absolute等定位方式,因此为了便于效果显示,所有的demo都把body的margin属性设为10px,并且增加了一个class为container的div元素,它具有10px的padding,3px的border,500px的width,并通过设置float:left,_zoom:1来形成一个BFC从而清除浮动。
2.2.5 非置换的浮动元素
首先,margin-left、margin-right的auto值将被设为0。
然后,元素的宽度则根据"shrink-to-fit"方法来计算,我们看一下"shrink-to-fit"算法的计算过程:
[1]计算preferred-width:在 除非包含的内容有明确的换行符(比如有
标签时),否则就不换行的情况下,容纳其包含的内容所需要的宽度。
[2]计算preferred-min-width:在 能换行时(英文碰到空格或标点符号,出现了块级元素,当然也包含出现了
标签时)就换行的情况下,容纳所包含的内容需要的宽度。
[3]计算available-width:即利用2.2.3节中的公式:
available-width =width of containing block - 'margin-left' - 'border-left-width' - 'padding-left' - 'padding-right' - 'border-right-width' - 'margin-right,这里也包括所有滚动出去的宽度。
最终的width则为:min(preferred-width, max(preferred-min-width, available-width))。最终的公式可以总结为:最终的宽度以available-width为基准,同时保证不能大于preferred-width以及不能小于preferred-min-width
available-width应该还比较容易理解,但是preferred-width和preferred-min-width相对不好理解一下,我们通过DEMO来分析一下:
##DEMO 1 shrink-to-fit算法示例一
我们有如下代码:
<<strong>div </strong><strong>class=</strong><strong>"container"</strong>><br /><<strong>div </strong><strong>style=</strong><strong>"</strong><strong>float</strong>: <strong>left</strong>;<strong>border</strong>: 1<strong>px solid </strong><strong>#f00</strong>;<strong>"</strong>><br /> dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd<br /></<strong>div</strong>><br /></<strong>div</strong>>
分别标记一下各个宽度:
注:这个demo在IE6中的显示效果会有些不同,因为IE6下及时定义了框的width属性,但是还是有可能会被内容撑开,这个问题本文后面会进行介绍
由于英文单词不允许换行,所以在这里,preferred-min-width和preferred-width都是相同的,也就是说,这个"单词"有多长,那么这两个宽度值也是多少,在这里是756px(还有左右各1px的边框),而available-width很明显就是包含块的宽度了,这里是500px,带入shrink-to-fit的公式中计算即可得到最终的width值为756px。
##DEMO 2 shrink-to-fit算法示例二
我们对DEMO1中的代码进行下改动,也就是把DEMO1中的"单词"加几个空格:
<<strong>div </strong><strong>class=</strong><strong>"container"</strong>><br /><<strong>div </strong><strong>style=</strong><strong>"</strong><strong>float</strong>: <strong>left</strong>;<strong>border</strong>: 1<strong>px solid </strong><strong>#f00</strong>;<strong>"</strong>><br /> ddddddddddddddddddd dddddddddddd ddddddddddd dddddddddddddddddddddddddddddddddddddddddddddd<br /></<strong>div</strong>><br /></<strong>div</strong>>
标记一下各个宽度:
把这个几个值带入公式计算,也不难得出最终的宽度值就是available-width,即500px
2.2.6 浮动置换元素
而width属性的计算则与行内置换元素的计算方法相同,可参照2.2.2节中的介绍。
2.2.7 绝对定位的非置换元素
为了方便显示,我们为container设置了100px的height值,另外需要明确的是,对于绝对定位的元素来说,如果它的包含块是一个块容器框产生,那么包含块的宽度是这个框的padding edge所形成;如果是一个行内元素,那么由包围其行框的区域形成;另外还有可能是初始包含块,具体可参照《CSS2.1SPEC:视觉格式化模型之包含块》的3.3节。
在本节和下一节中,需要首先明确一个概念"static position":
the term "static position" (of an element) refers, roughly, to the position an element would have had in the normal
flow.
也就是,"static position"是指正常流中的第一个元素应该所处的位置(该元素的position属性为"static",并且没有float),这个元素其实是一个假想的元素。
此外,"left"值指的是包含块的左边缘到绝对定位元素所产生的框的左外边距边缘(left margin edge)的距离。"right"指的是包含块的右边缘到绝对定位元素所产生的框的右外边距的边缘(right margin edge)的距离。
我们用下面的demo演示一下包含块的direction为默认的ltr情况下的"static position":
##DEMO 3 "static position"说明
代码如下:
<<strong>div </strong><strong>class=</strong><strong>"container"</strong>><br /><<strong>div </strong><strong>style=</strong><strong>"</strong><strong>width</strong>: 100<strong>px</strong>;<strong>height</strong>: 100<strong>px</strong>;<strong>background</strong>: <strong>#34538b</strong>;<strong>"</strong>><br /> static posotion演示<br /></<strong>div</strong>><br /></<strong>div</strong>>
我们的container有10px的padding,对于正常流中的块级元素来说,在水平方向上,默认就是紧贴包含块的content area左侧的,而这个位置和content area的padding edge的距离就是padding-left。
标准中还指出,用户代理不一定必须去计算这个假想的box的尺寸。
另外, 这一节的内容都必须基于以下等式:
'left' + 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' + 'right' = width of containing block
=》 IF : left,right和width的值 都是auto,那么首先将margin-left、margin-right的auto设为0,如果包含块的direction属性为ltr,则把left值设为"static position"的位置,并按照下面的 规则3计算width和right的值;如果包含块的direction属性为rtl,则把right值设为"static position"的位置,并按照下面的
规则1
计算left和width的值。 [1]
=》ELSE IF:left,right和width的值都不是auto,如果margin-left和margin-right都是auto,则它们将会拥有相同的值并且必须使得等式成立,从而实现居中效果。但如果这样得出的margin值为负值,那么在包含块的direction属性为ltr的情况下需要将margin-left置为0,并且按照等式计算margin-right的值;反之则将margin-right的值置为0,然后按照等式计算margin-left的值。 [2]
=》ELSE IF:margin-left或者margin-right只有一个为auto,那么就按照等式计算auto值。[3]
=》ELSE IF:所有的值都不为auto(过约束),那么在包含块的direction属性为ltr的情况下,忽略right值,并按照等式重新计算right;反之则忽略left的值,并按照等式重新计算。[4]
=》ELSE IF:上面的几种情况如果都不符合,那么就先把margin的所有auto值置为0,然后按照下面的规则进行计算。[5]
规则:
[1]:如果left和width是auto,那么先按照"shrink-to-fit"算法计算width值,然后再根据等式计算left值。
[2]: 如果left和right都是auto,但是width不是auto,那么如果包含块的direction属性为ltr,则把left置为"static position"的位置,然后再根据等式求right值;反之,则把right置为"static position"的位置,然后再根据等式求left值。
[3]: 如果width和right是auto,那么首先根据"shrink-to-fit"算法计算width值,然后再根据等式计算right值
[4]: 标准原文中的4,5,6条规则其实可以归结为一条,即如果left,right和width只有一个auto值,那么直接根据等式计算auto值。
绝对定位元素的width计算相对复杂一些,我们通过几个demo来看一下:
##DEMO 4 width,left和right都为auto的情况
代码如下:
<<strong>div </strong><strong>class=</strong><strong>"container"</strong>><br /><<strong>div </strong><strong>style=</strong><strong>height</strong>: 100<strong>px</strong>;<strong>background</strong>: <strong>#34538b</strong>;<strong>position</strong>: <strong>absolute</strong><strong>"</strong>><br /> 绝对定位演示<br /></<strong>div</strong>><br /></<strong>div</strong>>
##DEMO 5 width,left和right都不为auto的情况
我们把demo4中的代码修改一下,为绝对定位元素设置显式的width,left和right值,并且为margin-left和margin-right设置auo值:
<<strong>div </strong><strong>class=</strong><strong>"container"</strong>><br /><<strong>div </strong><strong>style=</strong><strong>"</strong><strong>width</strong>:100<strong>px</strong>;<strong>height</strong>: 100<strong>px</strong>;<strong>background</strong>: <strong>#34538b</strong>;<strong>position</strong>: <strong>absolute</strong>;<strong>left</strong>: 0<strong>px</strong>;<strong>right</strong>: 0<strong>px</strong>;<strong>margin-left</strong>: <strong>auto</strong>;<strong>margin-right</strong>: <strong>auto</strong>;<strong>"</strong>><br /> 绝对定位演示<br /></<strong>div</strong>><br /></<strong>div</strong>>
可以明显看到,在width,left和right都不为auto,并且水平margin为auto时,会按照平分margin-left和margin-right的方式布局,从而也可以达到元素居中的效果。
注:IE6/7并不会按照这个规则计算margin值,而是直接将margin的auto置为0然后再进行计算。
我们再修改一下代码,看一下只有一个margin值为auto时的情况:
<<strong>div </strong><strong>class=</strong><strong>"container"</strong>><br /><<strong>div </strong><strong>style=</strong><strong>"</strong><strong>width</strong>:100<strong>px</strong>;<strong>height</strong>: 100<strong>px</strong>;<strong>background</strong>: <strong>#34538b</strong>;<strong>position</strong>: <strong>absolute</strong>;<strong>left</strong>: 0<strong>px</strong>;<strong>right</strong>: 0<strong>px</strong>;<strong>margin-left</strong>: <strong>auto</strong>;<strong>margin-right</strong>: 0<strong>px</strong>;<strong>"</strong>><br /> 绝对定位演示<br /></<strong>div</strong>><br /></<strong>div</strong>>
可以看到,margin-left值是根据公式计算得出的值。
注:IE6/7同样还是把margin的auto值置为0,然后等于是在过约束的条件下进行计算,right的值就要被重新计算了,显示效果如下:
这样同时验证了过约束条件下的right或left值被重新计算的规则。
绝对定位元素的width及left,margin-left,margin-right,right值的计算问题稍显复杂,但是如果理解清楚之后也是比较好理解的,建议大家都通过实验来验证一下。另外,IE6/7不管在哪一种情况下就会将margin值置为0的特点也要务必牢记。
2.2.8 绝对定位的置换元素
绝对定位的置换元素在计算时,上一节中关于"static position"以及等式都是适用的,但是计算逻辑并不相同,具体如下:
=》 IF : width是根据 “行内置换元素width值计算规则”计算得到。而如果margin-left和margin-right的计算值都为auto时,它们的使用值由如下规则计算得出。 [1]
=》ELSE IF:left,right的计算值都是auto,那么在包含块的direction属性为ltr的情况下需要将left置为"static position"的left位置;反之则把right值置为"static position"的right位置。 [2]
=》ELSE IF:left或right为auto,那么将margin-right和margin-left的auto值置为0。[3]
=》ELSE IF:此时如果margin-right和margin-left都为auto,则它们将会拥有相同的值并且必须使得等式成立,从而实现居中效果。但如果这样得出的margin值为负值,那么在包含块的direction属性为ltr的情况下需要将margin-left置为0,并且按照等式计算margin-right的值;反之则将margin-right的值置为0,然后按照等式计算margin-left的值。[4]
=》ELSE IF:此时如果只有一个值为auto,那么则按照等式计算auto值[5]
=》ELSE IF:此时如果形成了过约束,那么在包含块的direction属性为ltr的情况下,忽略right值,并按照等式重新计算right;反之则忽略left的值,并按照等式重新计算。[6]
这几个例子我们就不通过demo来实证了,大家可以亲自敲一敲代码来实验一下,当然IE6/7还是会把margin-left和margin-right的auto置为0。
2.2.9 非置换的行内块
display属性为"inline-block"的元素形成了一个行内块,基于我们已经了解过的规则,对于行内块,在计算margin-left,margin-right和width时就比较容易了,事实上,它的计算规则和浮动元素的计算规则是基本相同的。
首先,margin-left和margin-right的auto值被置为0;
然后,width值根据“shrink-to-fit"算法计算得到。
2.2.10 行内块置换元素
行内块置换元素的计算方法与2.2.2节行内置换元素的计算方法是完全相同的
3 min-width和max-width
在文章的开头部分曾提到,我们通过上述规则计算得出的width值只是一个tentative value,即暂定的一个值,原因就在于最终width的使用值可能还会受到min-width,和max-width值的影响,下面我们来介绍一下min-width和max-width。
3.1 min-width和max-width属性剖析
CSS标准中对min-width和max-width的定义如下:
These two properties allow authors to constrain content widths to a certain range.
这两个属性的作用就是可以让开发者将一个框的width限定在一个范围中,即[min-width:max-width]范围内。与width属性相同,这两个属性也不适用于行内非置换元素,表格行和表格行组,并且都不具有继承性,可以取绝对的长度值,也可以使用百分比,这些与width属性都是相似的,同时两个属性都不允许负值。
另外,min-width的默认值为0,即所有元素的width都不能小于0;max-width的默认值为none,即不限定元素width的最大值。
3.2 min-width,max-width的使用
min-width和max-width对于width使用值的具体影响如下:
[1]:首先根据第二节中介绍的规则计算得出width值以及相关的margin,left和right值。
[2]:如果计算得出的width值大于max-width,那么就把max-width作为width的使用值,再带入计算规则中计算一遍。
[3]:如果计算得出的width值小于了min-width,那么就把min-width作为width的使用值,再带入计算规则中计算一遍。
下面通过一个实例来实证一下,这里以max-width为例:
##DEMO 6
max-width的使用
代码如下:
<<strong>div </strong><strong>class=</strong><strong>"container"</strong>><br /><<strong>div </strong><strong>style=</strong><strong>"</strong><strong>height</strong>: 100<strong>px</strong>;<strong>background</strong>: <strong>#34538b</strong>;<strong>max-width</strong>: 400<strong>px</strong>;<strong>"</strong>><br /> max-width演示<br /></<strong>div</strong>><br /></<strong>div</strong>>