CSS样式理解提高

266 阅读7分钟

1.BFC渲染机制

1.1 BFC是什么

Block formatting context(简称 BFC) 块级格式化上下文,这是一个独立的渲染区域,只有块级盒子参与,它规定了内部的块级盒子如何布局,并且和外部不相干。

BFC就是一个管理块级元素的容器

1.2 创建BFC

以下方式会创建BFC

  1. 根元素(<html>)
  2. 浮动元素(float不是none
  3. 绝对定位元素(position为abosultefixed)
  4. 行内块元素(display为inline-block
  5. 表格单元格(display为table-cell,HTML表格单元格默认值)
  6. 表格标题(display为table-caption,HTML表格标题默认值)
  7. 匿名表格单元格元素( 标签table、row、tbody、thead、tfoot)
  8. overflow不为visible的块元素
  9. display实际flow-root的元素
  10. contain是layoutcontent或油漆的元素
  11. 弹性元素(display为flexinline-flex元素的子代元素
  12. 网格元素(display为gridinline-grid元素的子代元素
  13. 多列容器(元素的 column-countcolumn-width(en-US)不为 为 )auto,包括 column-count1
  14. column-span 为 all 的元素总是会创建一个新的 BFC,即使元素没有在一个多列容器中(标准变更铬错误

1.3 BFC布局

  • 内部块级元素独占一行
  • BFC不会与浮动盒子重叠(实现自适应的两栏布局
  • 内部的盒子垂直方向的距离由margin决定
  • 属于同一个BFC的两个相邻的box的margin会发生重叠(取最大的那个
  • 计算BFC的高度时,浮动元素也参与
  • BFC是页面上的一个隔离的独立容器,里面不会受到外界影响(同样不会影响外界)

1.4 BFC特性

特性1:BFC阻止垂直外边距重叠

BFC定义垂直方向距离由margin决定,在同一个BFC内,相邻或嵌套元素间没有隔档(border、padding等) 就会发生margin重叠。

  • 相邻兄弟元素margin重叠

    只需要在兄弟元素外面包裹一层容器,并触发该容器生成一个BFC。那么两个P便不属于同一个BFC,就不会发生margin重叠了

  • 嵌套父子margin重叠

    在wrap元素中添加:overflow:hidden;或者overflow:auto;使其父元素形成一个BFC;也可以在wrap元素中添加border:1px solid;或是padding:1px;这些都可以有效解决父子元素margin重叠问题。

特性2:BFC不会重叠浮动元素

利用这个特性可以创造双栏布局

<style>
    .box1 {
        height: 100px;
        width: 100px;
        float: left;
        background: lightblue;
    }
​
    .box2 {
        width: 300px;
        height: 300px;
        background: #eee;
        overflow: hidden;
    }
</style><body>
    <div class="box1">我是一个左浮动的元素</div>
    <div class="box2">
        我是正常盒子
    </div>
</body>

特性3:BFC可以包含浮动——清除浮动

元素浮动,脱离了文档流,父容器内容宽度为零(即发生高度塌陷),未能将子元素包裹住。解决这个问题,只需要把把父元素变成一个BFC就行了。常用的办法是给父元素设置overflow:hidden

<style>
  .box1 {
    width: 100px;
    height: 100px;
    float: left;
    border: 1px solid #000;
  }
​
  .box2 {
    width: 100px;
    height: 100px;
    float: left;
    border: 1px solid #000;
  }
​
  .box {
    background: yellow;
    overflow: hidden;
  }
</style><body>
  <div class="box">
    <div class="box1"></div>
    <div class="box2"></div>
  </div>
</body>

2.垂直水平居中的方式(不定宽高)

2.1 flex 布局

写法够简单直观,兼容性也没什么问题。

利用到了 2 个关键属性:justify-contentalign-items,都设置为 center,即可实现居中。(设置父元素属性,使唯一子元素居中

.flex-center {
    display: flex;
    justify-content: center;
    align-items: center;
}

2.2 flex + margin

flex 方法的变种;父级元素设置 flex,子元素设置 margin: auto;

可以理解为子元素被四周的 margin “挤” 到了中间。

<div class="wrapper">
    <p>horizontal and vertical</p>
</div>
<style>
.wrapper {
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
​
    display: flex;
}
​
.wrapper > p {
    margin: auto;
}
</style>

2.3 transform + absolute

这个组合,常用于图片的居中显示。

<div class="wrapper">
    <img src="test.png">
</div>
<style>
.wrapper {
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
    position: relative;
}
​
.wrapper > img {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
}
</style>

没有绝对定位,设置img为相对定位,其他属性不变,同样能居中。

2.4 absolute + 四个方向的值相等

使用绝对定位布局,设置 margin:auto;

并设置 top、left、right、bottom 的 值相等即可(不一定要都是 0);一般用于弹出层,需要设置弹出层的宽高。

<div class="wrapper">
    <p>hello web</p>
</div>
<style>
.wrapper {
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
    position: relative;
}
​
.wrapper > p {
    width: 170px;
    height: 20px;
    margin: auto;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
}
</style>

2.5 table-cell

利用 table 的单元格居中效果展示。

与 flex 一样,需要写在父级元素上。

<div class="wrapper">
    <p>hello web</p>
</div>
<style>
.wrapper {
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
​
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
</style>

2.6 writing-mode

该方法通过改变文字的排列方向,让文字先垂直在横向

<div class="wrapper">
    <div class="wrapper-inner">
        <p>horizontal and vertical</p>
    </div>
</div>
<style>
.wrapper {
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
    /* 文字垂直*/
    writing-mode: vertical-lr;
    text-align: center;
}
​
.wrapper > .wrapper-inner {
    /* 文字横向 */
    writing-mode: horizontal-tb;
    display: inline-block;
    text-align: center;
    width: 100%;
}
​
.wrapper > .wrapper-inner > p {
    display: inline-block;
    margin: auto;
    text-align: left;
}
</style>

2.7 网格布局grid

网格布局让我们能按行或者列来排序,从而能够达到垂直水平居中

兼容性不如flex;IE只支持10以上

<div class="wrapper">
    <p>horizontal and vertical</p>
</div>
<style>
.wrapper {
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
​
    display: grid;
}
​
.wrapper > p {
    align-self: center;
    justify-self: center;
}
</style>

2.8 伪元素 ::after

不设置元素的宽度,覆盖高度,元素设置基线的垂直居中

<div class="wrapper">
    <img src="test.png">
</div>
<style>
.wrapper {
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
    /* 水平居中 */
    text-align: center;
}
​
.wrapper::after {
    content: '';
    display: inline-block;
    /* 元素基线垂直居中 */
    vertical-align: middle;
    height: 100%;
}
​
.wrapper > img {
    vertical-align: middle;
}
</style>

2.9 伪元素::before

配合font-size展开的

<div class="wrapper">
    <img src="test.png">
</div>
<style>
.wrapper {
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
​
    text-align: center;
    font-size: 0;
}
​
.wrapper::before {
    display: inline-block;
    vertical-align: middle;
    font-size: 14px;
    content: '';
    height: 100%;
}
​
.wrapper > img {
    vertical-align: middle;
    font-size: 14px;
}
</style>

3. 深入理解CSS选择器优先级

3.1 什么是选择器优先级

浏览器通过优先级来判断哪一些属性值与一个元素最为相关,从而在该元素上应用这些属性值。优先级是基于不同种类选择器组成的匹配规则。

俗称:权重

3.2 优先级(权重)的计算规则

内联 > ID选择器 > 类选择器 > 标签选择器。

优先级是由 ABCD 的值来决定的,其中它们的值计算规则如下:

  1. 如果存在内联样式,那么 A = 1, 否则 A = 0;
  2. B 的值等于 ID选择器 出现的次数;
  1. C 的值等于 类选择器属性选择器伪类 出现的总次数;
  2. D 的值等于 标签选择器伪元素 出现的总次数 。
<div id="content" class="content">
我是什么颜色
</div>
#content {
    color:  red;
}
.content {
     color: white;
}

套用上面的算法,依次求出 A B C D 的值:

  1. 因为没有内联样式 ,所以 A = 0;
  2. ID选择器总共出现了1次, B = 1;
  1. 类选择器出现了1次, 属性选择器出现了0次,伪类选择器出现0次,所以 C = (1 + 0 + 0) = 1
  2. 标签选择器出现了0次, 伪元素出现了0次,所以 `D = (0 + 0) = 30;

上面算出的ABCD 可以简记作:(0, 1, 1, 0)

因此颜色是红色

练习一下计算

li                                  /* (0, 0, 0, 1) */
ul li                               /* (0, 0, 0, 2) */
ul ol+li                            /* (0, 0, 0, 3) */
ul ol+li                            /* (0, 0, 0, 3) */
h1 + *[REL=up]                      /* (0, 0, 1, 1) */
ul ol li.red                        /* (0, 0, 1, 3) */
li.red.level                        /* (0, 0, 2, 1) */
a1.a2.a3.a4.a5.a6.a7.a8.a9.a10.a11  /* (0, 0, 11,0) */
#x34y                               /* (0, 1, 0, 0) */
li:first-child h2 .title            /* (0, 0, 2, 2) */
#nav .selected > a:hover            /* (0, 1, 2, 1) */
html body #nav .selected > a:hover  /* (0, 1, 2, 3) */

3.3 优先级的特殊情况

内联样式的优先级是最高的,但是外部样式有没有什么办法覆盖内联样式呢?有的,那就要 !important 出马了。因为一般情况下,很少会使用内联样式 ,所以 !important 也很少会用到!如果不是为了要覆盖内联样式,建议尽量不要使用 !important

.box {  max-width: 100px;}
#box { width: 100px !important;}

使用!important踩的坑

.box 的宽度只有 100px , 而不是 300px, 可见,max-width 可以超越 width!important!但是,这实际上不是优先级的问题,因为优先级是比较相同属性的,而 max-widthwidth 是两个不同的属性。

优先级是同属性相互比较的

4. 清除浮动的多种方式

浮动的方式进行布局,这样就会使元素脱离文档流。而在文档流中,父元素会默认被子元素撑开,一旦子元素设置浮动,完全脱离文档流,就会导致子元素无法撑起父元素的高度,就会产生父元素高度塌陷问题。

4.1 开启BFC

BFC的特性:BFC可以包含浮动元素,浮动元素也参与高度计算

将父级元素变成BFC块级上下文(参考上面BFC内容),就能够解决该问题

代码示例

.parent-box {
  width: 200px;
  background: gray;
  border: 5px solid;
  overflow: hidden;
  zoom: 1;
}

4.2 父元素后面添加一个空的div

解决高度塌陷问题最简单的方法,直接在高度塌陷的父元素里最后面添加一个空的div,并将设置clear :both 属性,这样由于这个空的div并没浮动,所以可以撑开父元素高度,基本没有什么副作用。但是会在页面中添加多余的结构,不建议使用

<body>
   <div class="parent-box">
       <div class="son-box"></div>
       <div style="clear: both;"></div>   
   </div>
   <div class="another-box"></div>
<body>

4.3 给父元素设置高度

设置高度不能适应页面的快速变化;另外一种,父容器的高度可以通过内容撑开(比如img图片),实际当中此方法用的比较多。

4.4 ::after伪元素清除浮动(推荐)

不能适应页面的快速变化;另外一种,父容器的高度可以通过内容撑开(比如img图片),实际当中此方法用的比较多。

和第二种方式原理一样,但不会产生多余结构

.parent-box {      
  width: 200px;      
  background: gray;      
  border: 5px solid;    
}    
.parent-box::after {      
  content: "";      
  display: block;      
  clear: both;    
}

4.5 父元素内添加br标签

br标签存在一个属性clear,在br标签中设置属性clear="all",即能清除浮动。但这种方法与第二种一样,同样会使页面结构冗余。

<body>
   <div class="parent-box">
       <div class="son-box"></div>
       <br clear="all" />  
   </div>
   <div class="another-box"></div>
<body>