今天跟大家分享一下CSS中一些比较重要和比较容易被忽略的东西,开始吧。
样式优先级
当你在不同地方不同的选择器中对同一个元素属性添加了不同的样式的时候,该如何判断最后哪个样式会作用到元素上呢?判断的依据就是样式的优先级。样式优先级的判断分为三个步骤:
-
首先,根据样式的来源和重要性进行分组,一共可以分成五组(优先级从高到低)
- 用户自定义样式中带!important的样式
- 作者样式中带!important的样式
- 作者样式
- 用户自定义样式
- 浏览器样式
-
然后,如果在第一步中无法分出胜负,那么就根据选择器的权重进行判断。
每种CSS选择器都有各自的权重,权重从大到小排列分别是:- ID选择器
- 类选择器,伪类选择器,属性选择器
- 标签选择器,伪元素选择器
在计算样式优先级的时候除了考虑选择器的权重以外,当然还需要考虑内联样式和外联样式,很明显内联样式的优先级比外联的高,那么它的权重也大。综合这两种情况我们就有下面这样的计算方式:
行内样式 1,0,0,0
ID选择器 0,n,0,0
类选择器,伪类选择器,属性选择器 0,0,n,0
标签选择器,伪元素选择器 0,0,0,n
在上面n代表对应选择器的个数。根据这个方法,每组选择器都会有一个这样的四位数来表示权重,哪个选择器的数字权重越大它们的样式优先级就越高。比如下面几组选择器的权重
div .one{} 0,0,1,1
div #one{} 0,1,0,1
div a[href="one"] #one{}0,1,1,2
在这一步计算样式优先级的时候有两个特殊情况,一个是通配符选择器它的权重是0,0,0,0
,另一个是继承的样式没有优先级,也就是说它连0都没有。这两个特殊情况就会造成一种现象:
#out *{
color:green;
}
#out #in{
color:black;
}
<div id="out">
<div id="in">
<div id="iin">TEST</div>
</div>
</div>
上面代码的结果是字体颜色为绿色而不是很多人想的黑色。在这段代码中,TEST有两个color样式,一个是#out *中的,一个是从#in中继承的,最后前者取得了胜利,就是因为继承的样式没有优先级,它连通配符都不如。所以说在重置样式的时候使用通配符不是一个好的习惯,因为它可能会带来一些奇怪的现象。
- 最后,如果第二步之后还是没办法区分出优先级的话,那么就很简单了,后出现的优先级更高。
在平时的开发中,我们应该善用样式的优先级规则,不能一味的通过增加选择器权重来覆盖样式,这样长久之后会使得代码变得无法维护。
参考
- http://www.smashingmagazine.com/2010/11/02/the-important-css-declaration-how-and-when-to-use-it/
- http://www.smashingmagazine.com/2010/04/07/css-specificity-and-inheritance/
- http://css-tricks.com/specifics-on-css-specificity/
- http://snook.ca/archives/html_and_css/understanding_c
块级元素
块级元素通常独占一行并且在正常流中垂直摆放,可以设置高度和宽度。块级元素在格式化的时候可以分为水平格式化和垂直格式化。
在水平方向上,元素的所有横向宽度之和要等于包含块的宽度,也就是要满足下面的等式:margin-left+border-left-width+padding-left+width+padding-right+border-right-right+margin-right=包含块的width
。
当width为auto时,块级元素的宽度会填满整个包含块。当margin-left和margin-right出现auto时,会根据上面公式计算使得margin-left=margin-right,这也就是设置margin为auto时可以实现居中效果的原因。
在垂直方向上,情况就比水平方向上要复杂,因为可能会出现外边距叠加的现象。
当height为auto时,跟水平方向不同,height会根据内容的高度来计算。而margin-top和margin-bottom为auto时,会被设置为0。
下面来说说垂直方向上外边距叠加的情况。首先,什么是外边距叠加?
外边距叠加是指在正常流中毗邻的两个以上块级元素在垂直方向上的外边距发生叠加的现象。
根据上面的定义我们可以发生外边距叠加需要下面几个条件:
1. 元素必须在正常流中,也就是说position不为absolute或fixed,并且没有浮动的元素
2. 元素必须是块级元素,很明显在行内元素上设置上下外边距是没有效果的
3. 发生外边距叠加的元素必须是毗邻的,也就是说两个元素是相邻的两个兄弟元素或者是父子关系的元素,而父子关系中必须是父元素和它的第一个子元素或者最后一个子元素。总结起来就是下面四种情况:
- 父元素的margin-top和它第一个子元素的margin-top
- 父元素的margin-bottom和它最后一个子元素的margin-bottom
- 元素的margin-bottom和它相邻的兄弟节点的margin-top
- 元素自身的margin-top和margin-bottom
下面来看前三种情况:DEMO
很明显,父元素#out的上外边距和第一个子元素#first的上外边距发生了重叠;然后#first的下外边距和它的兄弟#last的上外边距发生了叠加;最后,父元素#out的下外边距和最后一个子元素#last的下外边距发生了叠加。
对于外边距叠加,我觉得下面参考中smallni的总结很好,我就借用他的总结一下:
- 父元素和它的第一个子元素
- 父元素没有创建BFC
- 父子元素之间没有非空内容
- 父元素没有padding-top和border-top
- 父元素和它的最后一个子元素
- 父元素没有创建BFC
- 父子元素之间没有非空内容
- 父元素没有padding-bottom和border-bottom
- 父元素的height为auto,min-height为0
- 兄弟元素之间
- 兄弟元素都不是float元素
- 兄弟元素都不是absolute元素
- 兄弟元素都不是inline-block元素
根据上面的总结,我们也可以得出防止外边距叠加的方法:对于父子元素之间的情况,大多数情况下都是采用让父元素创建BFC的方式,对于BFC的概念大家可以google一下,这个也是CSS中一个很重要的概念,当然还有一个办法就是添加padding或border;而对于兄弟元素之间的情况,破坏那三条中间任意一条就可以了。
讲了外边距叠加,那在发生外边距叠加的时候,元素之间的外边距该怎么计算了,这也需要分几种情况讨论。
+ 如果发生叠加的外边距都是正值,那么就取两者中间最大的那个作为最后的边距
+ 如果发生叠加的外边距都是负值,那么就取两者绝对值较大的那个然后进行负向位移
+ 如果发生叠加的外边距有正有负,那么就把负值中间绝对值最大的那个和正值中最大的相加,最后得出的就是最后的边距
最后在进行计算的时候,所有相邻的外边距应该一起参与计算,不能分别进行计算然后再加起来。比如下面的情况:
<div style="margin:50px 0; background-color:green; width:50px;">
<div style="margin:-60px 0;">
<div style="margin:150px 0;">A</div>
</div>
</div>
<div style="margin:-100px 0; background-color:green; width:50px;">
<div style="margin:-120px 0;">
<div style="margin:200px 0;">B</div>
</div>
</div>
正确的计算A和B之间边距的方法应该是,取出所有负值然后找绝对值最大的也就是-120,然后在找出正值中最大的200,最后把这两个边距相加得出80,也就是最后A和B之间的边距。
参考
行内元素
行内元素在摆放的时候不会产生换行,也就是说行内元素在空间足够的情况下会出现在同一行中。行内元素有几个比较重要的概念:
1. 对于行内元素设置高度和宽度是无效的
2. 内容区:由内容高度所决定的区域
3. 行内框:对于非替换元素(<a>
,<span>
...)高度由line-height决定,对于替换元素(<img>
)高度由内容和内外边距决定
4. 内容区的上下加上line-height与font-size差值的一半就形成了行内框
5. vertical-align是根据行内框进行对其的
记住上面几个概念,对于行内元素的布局应该就能明白了。
边距和边框
外边距
外边距是什么我就不多说了,这里提几个比较容易误会的地方。
首先,当外边距是百分比时,它是根据父元素的宽度进行计算的,上下边距也是根据这个值进行计算。也就是说上下外边距的值也是以父元素的宽度为标准的。
其次,大家有时候书写margin属性的时候都喜欢使用缩写的方式,写两个宽度或者一个,可是如果写上三个的话会怎么样呢?浏览器对于外边距的计算有下面几个规则:
- 当缺少下边距时,使用上边距
- 当缺少左边距时,使用右边距
- 当缺少右边距时,使用上边距
用上面这三条规则就可以很好的解释边距缩写的原理了。这个原则对于所有边距和边框的缩写都适用。
内边距
对于内边距唯一要说的就是IE盒模型和W3C的盒模型的区别,在IE中padding和border是包括在元素width和height中的,而W3C的盒模型是不包括这两个的。还有一个问题就是内边距为百分比时的计算和内边距一样,都是以父元素的宽度为标准。
边框
边框有一个特点就是元素的背景会延续到边框,也就是说边框是覆盖在元素背景之上的。
浮动和定位
包含块
元素的包含块就是包含元素的那个元素,但是对于不同的元素情况会不一样:
- 浮动元素的包含块是最近的块级元素祖先的内容区域
- 绝对定位元素的包含块是最近的有定位的祖先。如果这个祖先是块级元素,那么包含块就是块级元素的边框区域;如果是行内元素,那么包含块就是行内元素的内容区域。
- 相对定位和static定位的元素的包含块就是最近的块级元素或者行内元素的内容区域
浮动
- 浮动元素会形成块级元素
- 浮动元素和块级元素重叠时,块级元素的背景在浮动元素下,而内容在上
- 浮动元素和行内元素重叠时,行内元素在上
看DEMO
从上面例子很容易看出浮动元素和块级元素重叠的时候不同的现象。
绝对定位
对于绝对定位在横向方向上也有一个计算公式:
left+(margin-left)+(border-left-width)+(padding-left)+width-(padding-right)+(border-right-width)+
(margin-right)+right=包含块width
这个公式在同时指定了绝对定位并且还指定了外边距的情况下非常有用。如果绝对定位不指定偏移,那么元素会怎么布局呢?很多人认为是相对于包含块进行定位,其实不是这样的。如果不指定偏移的话,元素会根据原来正常流中的位置进行定位。
DEMO
如果你在上面添加一个left偏移,你会发现它是在正常流的情况下发生偏移的,只有同时指定了两个方向的偏移,才会根据包含块进行定位。如果绝对定位的元素没有指定width和height那么我们可以通过设置偏移来对元素进行拉伸。
DEMO
通过修改偏移,你就可以控制元素的大小。-
在同时具有margin和偏移的时候该怎么给元素定位呢?
- 非替换元素
- 如果left,right,width都为auto,那么按照正常流方式进行布局,并且把值为auto的margin-left和margin-right设为0
- 如果left,right,width都不为auto
- 如果margin-left和margin-right都为auto,那么根据公式把两个设为相同的长度;如果计算出的长度为负数,那么就把margin-left设为0
- 如果margin-lef或margin-right其中有一个为auto,那么根据公式计算出auto的值
- 其他情况,首先把值为auto的margin设为0
- 如果left不为auto,width根据内容计算值,其他值根据公式进行计算
- 如果width不为auto,首先按照正常流定位,然后根据公式计算margin的值
- 如果right不为auto,width根据内容计算,其他值根据公式进行计算
- 如果left,width不为auto,right根据公式计算
- 如果left,right不为auto,width的值根据公式计算
- 如果width,right不为auto,left根据公式计算
- 替换元素
- 宽度由内容区决定
- 如果left和right为auto,那么根据正常流定位
- 如果left或right为auto,则把对于的值为auto的margin改为0
- 如果此时,margin都是auto,那么根据公式计算平方剩余长度
- 如果这时候left还是auto,那么根据公式计算left的值
上面这些计算原则就可以解释一个现象,就是之前在网上看到一种绝对居中的方法:
DEMO
这个方法把绝对定位元素的偏移都设为0,然后通过margin:auto
来实现居中,这个方法用上面的原则就可以说通了。
- 非替换元素
相对定位
相对定位相对来说就比较容易了。就两个点:
+ 偏移是根据元素本来在正常流中的位置计算的
+ 相对定位会影响其他元素的定位,相对定位元素会占据它原来的空间
PS:这篇博客大家看得可能有点痛苦,貌似现在SegmentFault不支持CodePen,不知道大家有没有什么办法可以做到。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。