CSS中一些属性的计算规则
昨天遇见了一个关于flex-shrink
计算规则的问题,一时语塞,发现自己居然没有认真研究过这里面的东西,因此在合理整理了关于CSS中一些属性的计算规则。
本文主要参考《CSS权威指南》和MDN上相关文档,整理一些属性的计算规则,因此章节顺序不影响阅读,但可能需要提前了解如BFC
、包含块之类的概念。
padding百分比
参考:包含块
一个元素的包含块由其position
属性确定。
- 如果 position 属性为 static 或 relative ,包含块就是由它的最近的祖先块元素
- 如果 position 属性为 absolute ,包含块就是由它的最近的 position 的值不是 static 的祖先元素的内边距区的边缘组成。
- 如果 position 属性是 fixed,包含块就是当前窗口
如果某些属性被赋予一个百分值的话,它的计算值是由这个元素的包含块计算而来的。这些属性包括盒模型属性和偏移属性:
- 要计算 height top 及 bottom ,这些属性由包含块的 height 属性的值来计算它的百分值。如果包含块的 height 值会根据它的内容变化,而且包含块的 position 属性的值被赋予 relative 或 static ,那么,这些值的计算值为 0。
- 要计算 width, left, right, padding, margin 这些属性由包含块的 width 属性的值来计算它的百分值。
如padding百分比的计算规则:当设置padding时,如果使用百分数,其取值是相对于其包含块的宽度进行计算的。
基于此规则,我们可以实现一个宽高成比例的容器(如正方形)。下面是一个实现正方形容器的示例
<style>
/* 当容器的width非固定值时就很有效 */
.box {background-color: red;position: relative;width: 100px;}
/* 由after撑开父元素 */
.box::after {
content: '';display: block;width: 100%;
padding-top: 100%; /* 此处就可以实现宽高按比例设置 */
}
/* 具体的内容在box_inner编写 */
.box_inner {position: absolute;z-index: 1;left: 0;top: 0;width: 100%;height: 100%;}
</style>
<div class="box">
<div class="box_inner">content in here</div>
</div>
定位时过分受限
定位相关属性top
,right
,bottom
,left
描述了距离包含块对应边的偏移。
当同时声明了left和right会自动计算width的大小;当同时声明了top和bottom会自动计算height的大小;
如果同时设置了top
,right
,bottom
,left
,width
,height
,然后指定了margin:auto
就会导致元素出现过分受限,此时浏览器都会忽略偏移量指定值,并重新计算,因此可以利用这个规则实现垂直居中
.box {
position: absolute;
width: 100px;height: 100px;
left: 0;right: 0;top: 0;bottom: 0;
margin: auto 0;
}
line-height
line-height计算规则如下:
- 如果使用 em,ex 和百分数直接指定当前元素的行高,都是相对于当前元素的
font-size
进行计算 - 如果是从父元素继承行高:
- 如果父元素指定的是固定数值,则直接使用该值
- 如果父元素使用百分数来设置行高,浏览器会首先计算其父元素的字体大小与对应百分数的乘积,得到对应的结果再传递给对应元素作为行高,* 如果父元素使用em,与使用百分数的情形相同
- 如果父元素使用无单位的乘积因子来指定行高,浏览器会计算该元素的字体大小,然后乘以对应的乘积因子,并将结果应用在行高之上。
基于以上原因,MDN上的示例代码推荐使用无单位的乘积因子来设置需要继承的行高。
下面是一个例子:求下面s1、s2、s5、s6的font-size和line-height分别是多少px
.p1 {font-size: 16px; line-height: 32px;}
.s1 {font-size: 2em;} // 直接继承父元素的行高32px
.s2 {font-size: 2em; line-height: 2em;} // 根据当前元素的font-size设置行高 (16*2)*2
.p2 {font-size: 16px; line-height: 2;}
.s5 {font-size: 2em;} // 直接继承父元素的行高为乘积因子2,然后使用当前元素的font-size参与计算 2em * 2
.s6 {font-size: 2em; line-height: 2em;} //同上,根据当前元素的font-size计算行高
.p3 {font-size: 16px; line-height: 2em}
.s5 {font-size: 2em;} // 父元素行高设置的是em,先在父元素进行计算2*16,然后再将该结果应用在子元素上
.s6 {font-size: 2em; line-height: 2em} //同上,根据当前元素的font-size计算行高
<div class="p1">
<div class="s1">1</div> <!--32px 32px-->
<div class="s2">1</div> <!--32px 64px-->
</div>
<div class="p2">
<div class="s5">1</div> <!--32px 64px-->
<div class="s6">1</div> <!--32px 64px-->
</div>
<div class="p3">
<div class="s5">1</div><!-- 32px 32px -->
<div class="s6">1</div><!-- 32px 64px -->
</div>
margin外边距重叠
margin的计算规则如下,参考:margin - MDN
- 如果以百分比为单位设置四个方向上的margin值,计算时取的相对于该元素的包含块的宽度与该百分比乘积的结果
- 元素的margin-top/margin-left被赋予负值时,元素将被拉进指定的方向,覆盖之前的元素
- 设置margin-bottom/right为负数,元素并不会向下/右移动,而是将后续的元素拖拉进来,覆盖本来的元素
- margin的top和bottom属性对非替换内联元素无效
collapsing-margin
,两个或多个毗邻的普通流中的块元素垂直方向上的 margin 会重叠
假设两个元素box1、box2,其中box1的margin-bottom
为mb,box2的margin-top
为mt,则发生外边距重叠时,这两个元素之间的距离按照下面标准取舍:
if(mb > 0 && mt > 0){
// 全部都为正值,取最大者
ans = Math.max(mb, mt)
}else if(mb < 0 && mt < 0){
// 全部都是负值,则都取绝对值,然后用0减去最大绝对值
ans = 0 - Math.max(Math.abs(mb), Math.abs(mt))
}else {
// 不全是正值,则都取绝对值,然后用正值减去最大绝对值;
ans = findPositive(mb, mt) - Math.max(Math.abs(mb), Math.abs(mt))
}
下面是一个例子
<style>
.box1 {height: 200px;background-color: red;margin-bottom: 20px}
.box2 {height: 200px;background-color: blue;margin-top: 30px}
</style>
<div class="wrap">
<div class="box1"></div>
<div class="box2"></div>
</div>
当
mb=20, mt=30
时,box1和box2之间的间距为Math.max(20, 30)
,即30mb=20, mt=-30
时,box1和box2之间的间距为正值20 - 最大绝对值30
,即-10mb=-20,mt=-30
时,box1和box2之间的间距为0 - 最大绝对值30
,即-30
flex-grow
当容器有剩余宽度时,子元素将按照设置的flex-grow
属性比例等分剩余空间。
假设父元素宽度w,三个子元素宽度分别为w1,w2,w3,,三个子元素的flex-grow
分别为 a,b,c,则计算公式为
x = w - (w1 + w2 + w3) // 剩余的宽度,分配给子元素
sum = a + b + c // flex-grow 总权重
if(sum < 1){
sum = 1 // 注意sum小于1时按1计算,剩余空间不会分配
}
// 第一个子元素可分 x * a / sum,计算后的宽度为 w1 + x * a / sum
// 第二个子元素可分 x * b / sum,计算后的宽度为 w2 + x * b / sum
// 第三个子元素可分 x * c / sum,计算后的宽度为 w3 + x * c / sum
下面是一个例子
<style>
div {height: 200px}
.f {display: flex;width: 600px}
.c1 {width: 200px;flex-grow: 1;background-color: red}
.c2 {width: 300px;flex-grow: 2;background-color: blue}
</style>
<div class="f">
<!-- 200 + 1/(1+2)*(600-200-300) = 233.33 -->
<div class="c1"></div>
<!-- 300 + 2/(1+2)*(600-200-300) = 366.67 -->
<div class="c2"></div>
</div>
flex-shrink
仅在子元素默认宽度之和大于容器的时候才会发生收缩,其收缩的大小是依据 flex-shrink 的值。
假设父元素宽度w,三个子元素宽度分别为w1,w2,w3,,三个子元素的flex-shrink
分别为 a,b,c,则计算公式为
x = (w1 + w2 + w3) - w // 溢出的宽度,由每个子元素收缩一部分
if (a + b + c < 1){
// 当 flex-shrink 之和小于 1 时,只会收缩一部分宽度,其余仍会溢出容器
x = (a + b + c) * x
}
sum = a*w1 + b*w2 + c*w3 // flex-shrink总权重,
// 第一个元素收缩 x * a * w1 / sum
// 第二个元素收缩 x * b * w2 / sum
// 第三个元素收缩 x * c * w3 / sum
下面是一个示例
<style>
div {height: 200px}
.f {display: flex;width: 600px}
.c1 {width: 500px;flex-shrink: 1;background-color: red}
.c2 {width: 400px;flex-shrink: 2;background-color: blue}
</style>
<div class="f">
<!--500 - 300 * 1 * 500 / (1*500+2*400) = 384.61 -->
<div class="c1"></div>
<!--400 - 300 * 2 * 400 / (1*500+2*400) = 215.39 -->
<div class="c2"></div>
</div>
小结
现在写css时,基本上都只是为了快速完成页面的编写,忽略了很多属性的运行原理和计算规则,只满足于完成任务而已,这是一个很危险的信号。
本文整理了一些常用或容易被忽略的样式计算规则,只有了解了规则的计算原理,才能更好更快地编写合格的样式表。
你要请我喝一杯奶茶?
版权声明:自由转载-非商用-保持署名和原文链接。
本站文章均为本人原创,参考文章我都会在文中进行声明,也请您转载时附上署名。