CSS

# width:auto 和 height:auto

width、height的默认值为auto。

width: auto

  • 对块级元素来说,流体布局(即正常流)下width自适应撑满父元素宽度,不同于width:100%的固定宽度,它可以根据margin不同而自适应父元素的宽度。
  • 对内联元素来说,width由子元素宽度决定

height: auto

  • 无论是块级元素还是内联元素,高度由子级元素撑开
  • 父元素height: auto会使子元素height:100%百分比失效。如果想要子元素百分比生效,父元素需要设定显式高度值;或使用绝对定位

注:绝对定位元素百分比根据padding box计算,非绝对定位元素百分比是根据content box计算。

# CSS权重

image-20220225210023698

小栗子:

// 假设下面样式都作用于同一个节点元素`span`,判断下面哪个样式会生效
body#god div.dad span.son {width: 200px;}
body#god span#test {width: 250px;}
1
2
3

第一个样式权重计算是(body+div+span=3) + (#god+.dad+.son=120) = 123;第二个样式权重计算是(body+span=2) + (#god+#test=200) = 202202 > 123所以最终计算结果是取width: 250px;

注:

  1. 在css中,!important的权重相当的高,但是由于宽高会被max-width/min-width覆盖,所以有时!important会失效。比如width: 100px !important; min-width: 200px;最终会被引擎解析为width: 200px。
  2. 虽然在上图给了权重的相应值,但是10个类选择器还是没有一个id选择器权重大,上图给出具体数值只是为了更好的理解。

# css盒子模型

image-20220116131603013

盒子模型由Content + Padding + Border + Margin组成。盒模型分为W3C标准盒模型IE盒模型两种:

  • W3C标准盒模型的 width 和 height 只包含 content
  • IE盒模型的 width 和 height 为 content + padding + border

  1. 虽然CSS盒模型包括 margin,但盒子大小是没有将 margin 算进去的。
  2. 在 IE8+ 浏览器中使用哪个盒模型可以通过box-sizing(CSS新增属性)控制,默认值为 content-box (标准盒模型),如果设为 border-box 则用的是IE盒模型。
  3. 如果不声明 DOCTYPE 类型,IE浏览器会将盒模型解释为IE盒模型,而其他浏览器则解释为W3C盒模型,如果加上 DOCTYPE 声明,则所有浏览器都会默认为W3C盒模型。

# 关于padding、margin的几点说明

padding 不可为负值,但是可以为百分比值。为百分比时水平和垂直方向的 padding 都是相对于父级元素宽度计算的。将一个div设为padding: 100%就能得到一个正方形,padding: 10% 50%可以得到一个宽高比 5:1 的矩形。

margin

  • margin负值原理

    • 这里要引入的margin参考线的概念,参考线就是margin移动的基准点。参考线分为两类:一类是top、left,它们以外元素作为参考线;一类是right、bottom,它们以自身作为参考线。

      • top负值就是以包含块内容区域的上边或上方相连元素margin的下边为参考线(影响自身及其他元素位置,此处有点像引体向上,基线不变,盒子把自己撑上去了)
      • left负值就是以包含块内容区域的左边或左方相连元素margin的右边为参考线
      • right负值就是以元素本身border的右边为参考线
      • bottom负值就是以元素本身border的下边为参考线(下面元素的位置上移)

      image-20220226154228004

      总地来说,就是当margin-top、left为负值的时候与参考线的距离减少,当margin-right、bottom为负值的时候参考线就向左、上面移动。

      <div class="box">
        <div class="one">one</div>
        <div class="two">two</div>
        111
      </div>
      <style>
        .box {
          width: 200px;
          height: 200px;
          border: 1px black solid;
        }
        .box div {
          width: 100px;
          height: 100px;
        }
        .one {
          background: gray;
          /* margin-bottom: -50px; */
        }
        .two {
          background: orange;
          margin-top:-50px;
          /*与上面margin-bottom效果一样,只是作用对象不同*/
        }
      </style>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
  • 作为外边距,margin 属性并不会参与盒子宽度的计算,但通过设置margin的left or right为负值,能改变元素水平方向的尺寸(参考上面margin负值原理)。

    <div>test</div>
    <style>
    div{
    	margin:0 -100px;
    }
    </style>
    //文字消失
    
    1
    2
    3
    4
    5
    6
    7

    此时 div 元素的宽度是比父级元素的宽度大 200px 的。但左边多出的100px被隐藏,右边多出的100px使横向出现了滚动条,多出的位置刚好为100px。

    但是这种情况只会发生在元素是流布局的时候,即元素 width 是默认的 auto 并且可以撑满一行的时候。如果元素设定了宽度,或者元素设置了float: left / position: absolute这样的属性改变了流体布局,那么 margin 为负也无法改变元素的宽度了。

  • margin: auto能在块级元素设定宽高之后自动填充剩余宽高。

    margin: auto自动填充触发的前提条件是元素在对应的水平或垂直方向具有自动填充特性,显然默认情况下块级元素的高度是不具备这个条件的。

    典型应用是块级元素水平居中的实现:

    display:block
    width:200px;
    margin:0 auto;
    
    1
    2
    3

    auto的特性是,如果两侧都是 auto ,则两侧均分剩余宽度;如果一侧 margin 是固定的,另一侧是 auto ,则这一侧 auto 为剩余宽度。

    除了水平方向,垂直方向的 margin 也能实现垂直居中,但是需要元素在垂直方向具有自动填充特性,而这个特性可以利用position实现:

    position:absolute;
    left: 0; right: 0; top: 0; bottom: 0;
    width: 200px;
    height: 200px;
    margin: auto;
    
    1
    2
    3
    4
    5

# 流的破坏与保护

流式布局是 CSS 中最基本的定位和布局机制。浏览器中的元素默认从左到右(内联元素),从上到下(块级元素)如同流水一般堆砌的布局方式。

# float

float 的设计初衷是为了实现“文字环绕图片”的效果,但因为float的一些特性,导致其被到处使用以致于产生了诸多不利于维护的页面。 float 属性有以下特性:

  1. 包裹性:即元素的 width 和 height 一样由子元素决定,而不是默认撑满父元素
  2. 块状化并创建BFC:元素设置 float 之后,其 display 的计算值就成了 block,并创建一个BFC
  3. 没有任何 margin 合并
  4. 脱离文档流:float 的设计初衷是为了实现“文字环绕图片”的效果,为了让文字环绕图片,需要具备两个条件。一是元素高度坍塌,这样后面的非浮动块级盒子就会上移与其重叠,就像是脱离了文档流一样;二是行框盒子不可与浮动元素重叠,实现文字绕着图片的效果。

如果一个元素设置了float属性,则会出现父元素高度坍塌的效果,这是浮动元素脱离文档流,使包含它的无高度样式的父容器感知不到其存在而发生内容无法撑开高度的现象。就是因为脱离文档流这个特性,在开发中带来了很多麻烦,需要清除浮动。

# 清除浮动

元素浮动之前,也就是在标准流中,是竖向排列的,而浮动之后可以理解为横向排列。清除浮动可以理解为打破横向排列。清除浮动(clear)只能影响使用清除的元素本身,不能影响其他元素。

清除浮动的三种方法:

  1. 对父容器设置overflow: hidden/auto,触发其BFC,因为计算BFC的高度时,考虑BFC所包含的所有元素,连浮动元素也参与计算,这样就达到了清除浮动的目的。设置hidden会导致超出部分直接被隐藏,且不占据文档流位置,而设置auto的话超出部分会生成一个滚动条,影响视觉效果。
  2. 在浮动元素之后添加空标记,并对该标记应用“clear:both”样式,可清除元素浮动所产生的影响,这个空标记可以为<div><p><hr>等任何块级元素。
  3. 给父元素设置after伪对象也可以清除浮动,但是该方法只适用于IE8及以上版本浏览器和其他非IE浏览器。使用after伪对象清除浮动时需要注意以下两点:
    • 必须在伪对象中设置content属性,属性值可以为空,如“content: "";”。
    • 该伪类元素必须为block元素,原因是after伪元素在其父容器的其他标签的最后添加一个元素,渲染顺序排在了其父容器内部的最后,设置block元素是为了不让其它元素与其一排,再对其设置清除浮动,父容器自然就被这个after伪元素撑开了高度。

clear的作用和不足

clear 可以清除前后浮动元素的浮动,但实际上并不是真的清除了浮动。

clear 的定义是:元素盒子的边不能与前面的浮动元素相邻。虽然浮动元素高度坍塌,但是设置了 clear:both; 的元素却将其高度视为仍然占据位置。

clear 只能作用于块级元素,且并不能解决后面元素可能发生的文字环绕问题。

# 绝对定位

和浮动元素一样,绝对定位也有包裹性、块状化、BFC、没有margin合并、脱离文档流的特性。

但和浮动元素不同的是,绝对定位是完全脱离文档流。浮动产生的目的就是为了实现文字环绕效果,所以浮动元素虽然脱离了文档流,但是后面的文字还是会环绕在浮动元素周围。而绝对定位一但产生,就不会再对周围元素产生任何影响。

而且两者包含块不同,浮动元素包含块只能是父级元素,绝对定位的包含块则是距离最近的 position 不为 static 的祖先元素。

绝对定位和 overflow: hidden

overflow: hidden元素在绝对定位元素和其包含块之间的时候,绝对定位元素不会被剪裁。

以下两种绝对定位元素不会被剪裁:

<div style="overflow: hidden;">
	<img src="big.jpg" style="position: absolute;">
</div>
<div style="position: relative;">
	<div style="overflow: hidden;">
    	<img src="big.jpg" style="position: absolute;">
	</div>    
</div>
1
2
3
4
5
6
7
8

以下两种绝对定位元素会被剪裁:

<div style="overflow: hidden; position: relative;">
	<img src="big.jpg" style="position: absolute;">
</div>
<div style="overflow: hidden;">
	<div style="position: relative;">
    <img src="big.jpg" style="position: absolute;">
  </div>    
</div>
1
2
3
4
5
6
7
8

position: absolute的流体特性

当绝对定位元素的水平方向(left/right)或垂直方向(top/bottom)的两个定位属性同时存在的时候,绝对元素在该方向上便具有了流体特性。此时的 width/height 属性具有自动撑满的特性,和一个正常流的 div 元素的 width 属性别无二致。设置了固定 margin 值的元素,宽高 auto 能够自动适应剩余空间;同样的,设置了固定宽高的元素,如果margin: auto,则 margin 平分剩余空间导致垂直水平居中。

# 块格式化上下文BFC

FC(Formatting Context)是W3C CSS2.1规范中的一个概念。它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系、相互作用。最常见的有BFC(B为Block)和IFC(I为inline),CSS3中还增加了GFC(G为grid)和FFC(F为flex)。

BFC直译为“块级格式化上下文”。它是一个独立的渲染区域,只有块级盒子参与,它规定了内部的块级盒子如何布局,并且与这个区域外部毫不相干。

创建块格式化上下文

  • 根元素html
  • 行内块元素
  • 浮动元素(float的值不为none)
  • 绝对定位元素(position的值为absolute或fixed)
  • 弹性元素(display的值为flex或inline-flex的直接子元素)
  • 网格元素(display的值为grid或inline-grid的直接子元素)
  • 表格单元格(元素的display: table-cell,HTML表格单元格默认属性)
  • 使用display: flow-root(无副作用创建BFC)
  • overflow的值不为visible
  • 详见MDN 块格式化上下文 (opens new window)

:display: table也认为可以生成BFC,因为table默认生成一个匿名的table-cell,正是这个匿名的table-cell生成了BFC。

BFC特性

几个重要的点

  1. BFC本身不会发生margin重叠,只会发生在属于同一BFC的块级元素之间
  2. BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然
  3. 每一个盒子的左外边距应该和包含块的左边缘相接触。即使存在浮动也是如此,除非子盒子形成了一个新的BFC(暂时还不是很理解)
  4. 计算BFC的高度时,考虑BFC所包含的所有元素,连浮动元素也参与计算
  5. BFC的区域不会与float box重叠
  6. 在父级块中使用 display: flow-root 可以创建新的无副作用的 BFC。这实际上是在创建一个行为类似于根元素的东西,里面将进行 flow layout

BFC应用

BFC可以彻底解决子元素浮动带来的高度坍塌和文字环绕问题。因为BFC是一个独立容器,与外界互不影响。它规定了BFC子元素无论margin-top: -10000px float: left 等都不会影响到BFC外部的元素的布局。所以BFC是最好的清除浮动的方式(利用特性4来解决元素高度坍塌的问题),连浮动的文字环绕问题都能解决。

  1. 清除浮动

    当容器内的元素浮动时,就会脱离文档流,且有时会发生父元素高度塌陷的情况。如果触发容器的BFC,那么容器将会包裹浮动元素(如给 .box 添加 display: inline-block;)。

    <head>
      <style>
        .box {
          border: 5px solid lightcoral;
          /* display: inline-block; */
        }
    
        .box1 {
          width: 100px;
          height: 100px;
          background: lightblue;
          float: left;
        }
      </style>
    </head>
    
    <body>
      <div class="box">
        <div class="box1"></div>
      </div>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
  2. 解决文字环绕问题(实现自适应两栏布局)

    BFC可以阻止元素被浮动元素覆盖。先看一个文字环绕的效果:

    <head>
      <style>
        .box {
          width: 200px;
          height: 200px;
          background-color: lightcoral;
          /* overflow: hidden; */
        }
    
        .box1 {
          width: 100px;
          height: 100px;
          background-color: lightblue;
          float: left;
        }
      </style>
    </head>
    
    <body>
      <div>
        <div class="box1">
          我是一个浮动的元素
        </div>
        <div class="box">
          我没有设置浮动,也没有触发BFC,假装这里文字很多,假装这里文字很多,假装这里文字很多,假装这里文字很多
        </div>
      </div>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28

    可以看到第二个div有部分被浮动元素遮挡,但文本信息会环绕着浮动元素,如果想避免元素被覆盖,可触发第二个元素的BFC特性,在 .box 中添加 overflow: hidden; 或 overflow: auto;。

    该方法可以用来实现两栏自适应布局,利用float+overflow:hidden;,主要思想是通过overflow触发BFC,而BFC不会与浮动元素重叠。由于设置overflow:hidden;并不会触发IE6-浏览器的haslayout属性,所以需要设置zoom:1来兼容。

# 外边距重叠

块级元素的垂直方向会发生margin合并,大小为单个边距的最大值,有三种情况会形成外边距重叠:

  1. 相邻两个元素之间的外边距折叠
  2. 没有内容将父元素和后代元素分开
  3. 空的块级元素

阻止margin合并:

  • 把元素放进BFC
  • 设置元素为floating、absolutely和inline-*
  • 给元素设置border、padding
  • 用行内元素(如文字)阻隔
  • 给父元素设置height(待进一步验证)

注意:

  1. 创建了BFC的元素如果设置了margin,除设置为float、position:absolute、行内块外其他都会发生外边距折叠,但如果将其放进另一个BFC元素中,则不会发生折叠。
  2. 就算父元素的外边距为0,第一个或最后一个子元素的外边距依然会“溢出”到父元素外面。
  3. 如果参与折叠的外边距中包含负值,则最终值为最大正边距+最小负边距;如果都为负边距,则去最小负边距的值。

# line-height和vertical-align

推荐一篇文章 (opens new window)

# 元素隐藏

  1. 如果希望元素不可见、不占据空间、资源会加载、不进入渲染树: display: none

  2. 如果希望元素不可见、不能点击、但占据空间、资源会加载、DOM 可访问,可以使用: visibility: hidden

  3. 如果希望元素不可见、不占据空间、显隐时可以用transition淡入淡出效果

    div{
    	position: absolute;
    	visibility: hidden;
    	opacity: 0;
    	transition: opacity .5s linear;
    	background: cyan;
    }
    div.active{
    	visibility: visible;
    	opacity: 1;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    这里使用 visibility: hidden 而不是 display: none ,是因为 display: none 会影响css3的 transition 过渡效果。 但是 display: none 并不会影响css animation 动画的效果。

  4. 如果希望元素不可见、可以点击、占据空间,可以使用: opacity: 0

  5. 如果希望元素不可见、可以点击、不占据空间,可以使用: opacity: 0; position: abolute;

  6. 如果希望元素不可见、不能点击、占据空间,可以使用: position: relative; z-index: -1;

  7. 如果希望元素不可见、不能点击、不占据空间,可以使用: position: absolute ; z-index: -1;

display,visibility,opacity

  1. display: none的元素不占据任何空间,visibility: hidden的元素空间保留;
  2. display: none会影响css3的transition过渡效果,visibility: hidden不会;
  3. display: none隐藏产生重绘 ( repaint ) 和回流 ( relfow ),性能消耗较大;visibility: hidden只会触发重绘;(回流重绘知识点参考:真正理解重绘和回流 (opens new window)),性能影响相对小
  4. 株连性:display: none的节点和子孙节点元素全都不可见,visibility: hidden的节点的子孙节点元素可以设置 visibility: visible显示。visibility: hidden属性值具有继承性,所以子孙元素默认继承了hidden而隐藏,但是当子孙元素重置为visibility: visible就不会被隐藏。
  5. opacity:0进入渲染树,占空间,也可以点击;显示亮度的问题,启用GPU加速;作为父元素,子元素不会继承属性

# 单行、多行文本溢出隐藏

  1. 单行文本溢出

    overflow: hidden;            // 溢出隐藏
    text-overflow: ellipsis;      // 溢出用省略号显示
    white-space: nowrap;         // 规定段落中的文本不进行换行
    
    1
    2
    3
  2. 多行文本溢出

    overflow: hidden;            // 溢出隐藏
    text-overflow: ellipsis;     // 溢出用省略号显示
    display:-webkit-box;         // 作为弹性伸缩盒子模型显示。
    -webkit-box-orient:vertical; // 设置伸缩盒子的子元素排列方式:从上到下垂直排列
    -webkit-line-clamp:3;        // 显示的行数
    
    1
    2
    3
    4
    5

注意:由于上面的三个属性都是 CSS3 的属性,没有浏览器可以兼容,所以要在前面加一个-webkit- 来兼容一部分浏览器。

white-space空白处理:

我们都知道如果在html中输入多个空白符,默认会被当成一个空白符处理,实际上就是这个属性控制的:

  1. normal:合并空白符和换行符,文本自动换行;
  2. nowrap:合并空白符,强制不换行(一行显示);
  3. pre:不合并空白符,并且只在有换行符的地方换行(如果没有换行符,即便溢出边框也不会自动换行);
  4. pre-wrap:不合并空白符,允许换行符换行和文本自动换行;
  5. pre-line:合并空白符,允许换行符换行和文本自动换行。

# 优雅降级、渐进增强

优雅降级:如果用户浏览器不支持一些东西,比如圆角、阴影,在保证功能的前提下,这些可以不实现

渐进增强:如果用户浏览器更高级,可以支持更多的东西,可以实现更多来提高用户体验

# 性能优化

  1. 合并css文件,如果页面加载10个css文件,每个文件1k,那么也要比只加载一个100k的css文件慢。
  2. 减少css嵌套,最好不要嵌套三层以上。
  3. 不要在ID选择器前面进行嵌套,ID本来就是唯一的而且权限值大,嵌套完全是浪费性能。
  4. 建立公共样式类,把相同样式提取出来作为公共类使用。
  5. 减少通配符*或者类似[hidden="true"]这类选择器的使用,挨个查找所有...这性能能好吗?
  6. 巧妙运用css的继承机制,如果父节点定义了,子节点就无需定义。
  7. 拆分出公共css文件,对于比较大的项目可以将大部分页面的公共结构样式提取出来放到单独css文件里,这样一次下载 后就放到缓存里,当然这种做法会增加请求,具体做法应以实际情况而定。
  8. 不用css表达式,表达式只是让你的代码显得更加酷炫,但是对性能的浪费可能是超乎你想象的。
  9. 少用css rest,可能会觉得重置样式是规范,但是其实其中有很多操作是不必要不友好的,有需求有兴趣,可以选择normolize.css。
  10. cssSprite,合成所有icon图片,用宽高加上background-position的背景图方式显现icon图,这样很实用,减少了http请求。
  11. 善后工作,css压缩(在线压缩工具 YUI Compressor)
  12. GZIP压缩,是一种流行的文件压缩算法。

# table布局

表格布局特点

  • 子容器默认自动平分宽度,且表格中的内容可以自动居中
  • 设置了一个 table-cell 为固定宽度之后,其余的自动平分占满容器

优点:兼容性好,布局相对简单,上手快

缺点

  1. table占用了更多的字节,下载时间延迟,占用服务器更多资源
  2. table会阻挡浏览器渲染引擎的顺序,页面生成速度延迟
  3. 灵活性较差,不易更改
  4. 不利于搜索引擎抓取信息,影响网站排名

# float布局

float布局仍然是当前用的比较多的一种布局方式。可以应用BFC不与浮动元素重叠的特性实现两栏和三栏布局。

优点:兼容性较好;

缺点:需要清除浮动,否则页面的布局会塌陷。

# flex布局

flexbox是一种一维的弹性布局模型。任何一个容器可以指定为flex布局(包括行内元素)。

注:设为flex布局后,子元素的floatclearvertical-align属性将失效。

详见Flex 布局教程:语法篇 (opens new window)

采用flex布局的元素简称为“容器”,容器的直系子元素就会变成“flex元素”。容器默认存在两根轴:主轴(main axis)和交叉轴(cross axis)。默认情况下,水平的为主轴,垂直的为交叉轴,项目默认沿主轴排列。其中,flex元素有以下行为:

  • 元素排列为一行(flex-direction属性的初始值是row),超出时会溢出容器而不会换行;
  • 元素从主轴的起始线开始;
  • 元素不会在主维度方向拉伸,但是可以缩小(空间不够被压缩);
  • 元素被拉伸来填充交叉轴大小;
  • flex-basis: auto;
  • flex-wrap: nowrap;

img

# 容器的属性

  1. flex-direction:决定主轴的方向,其中row为横向,column为纵向;

  2. flex-wrap:定义一行排不下是否换行,nowrap|wrap|wrap-reverse;

  3. flex-flow:是flex-direction和flex-wrap的简写形式。

  4. justify-content:定义了flex元素在主轴上的对齐方式

    • flex-start | flex-end | center | space-between | space-around

    image-20220419204525616

  5. align-items:定义flex元素在交叉轴上如何对齐

    • flex-start | flex-end | center | baseline | stretch
    • baseline:flex元素的第一行文字的基线对齐
    • stretch:(默认值)如果flex元素未设置高度或设为auto,将占满整个容器的高度。
  6. align-content:定义了多根轴线的对齐方式,如果flex元素只有一根轴线(只有一行或一列),该属性不起作用。

    img

# flex元素的属性

  1. order:元素的排列顺序,数值越小,排列越靠前,默认值为0。
  2. flex-grow:定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大
    • 如果所有项目的 flex-grow 属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的 flex-grow 属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。
  3. flex-shrink:定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
    • 如果所有项目的 flex-shrink 属性都为1,当空间不足时,都将等比例缩小。如果一个项目的 flex-shrink 属性为0,其他项目都为1,则空间不足时,前者不缩小。
    • 负值对该属性无效。
  4. flex-basis:定义了在分配多余空间之前,元素占据的主轴空间。
    • 浏览器根据这个属性,计算主轴是否有多余空间,它的默认值为auto,即元素的本来大小。
    • 它可以设为跟 width或 height 属性一样的值(比如350px),则项目将占据固定空间。
  5. flex:是前三个属性的简写,默认值为0 1 auto,后两个属性可选。
    • 属性快捷键:auto(1 1 auto) 和 none(0 0 auto)
    • 建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。
  6. align-self:可以定义单个元素与其他元素不同的对齐方式,可覆盖 align-items 属性,默认值为auto,表示继承父元素的 align-items 属性,如果没有父元素,则等同于 stretch 。
    • auto | flex-start | flex-end | center | baseline | stretch

优点:简单,灵活,对移动端友好

缺点:兼容性不是很好,一些浏览器不支持

# grid布局

flex布局一次只能处理一个维度上的元素布局,一行或一列;grid布局是二维布局,将容器划分成立行和列,产生一个个的网格,我们可以将网格元素放在与这些行和列相关的位置上,从而达到布局的目的。

Grid是最强大的css布局方案。设为网格布局后,容器子元素(项目)的floatdisplay:inline-blockdisplay:table-cellvertical-aligncolumn-*等设置都将失效。

详见CSS Grid 网格布局教程 (opens new window)

采用grid布局的元素简称为“容器”,容器的直系子元素为“网格项目”。Gird会创建编号的网格线来让我们定位每一个网格元素。

image.png

# 容器属性

  1. grid-template-columns | grid-template-rows

    • 分别用于定义列宽和行高,可以使用绝对单位或百分比

    • 如果有重复值可以使用repeat(重复次数,重复值)

    • auto-fill用于自动填充,使每一行或每一列容纳尽可能多的单元格,直到容器不能放置更多的列,如repeat(auto-fill, 100px);

    • fr关键字用于表示比例关系。

    • minmax(最小值,最大值)用于表示长度在这个范围之内。

    • auto关键字表示由浏览器自己决定长度。

    • 这两个属性里,还可以使用方括号来指定每一根网格线的名字,方便以后的引用,且允许同一根线有多个名字。

      .container {
        display: grid;
        grid-template-columns: [c1 othername] 100px [c2] 100px [c3] auto [c4];
        grid-template-rows: [r1] 100px [r2] 100px [r3] auto [r4];
      }
      
      1
      2
      3
      4
      5
  2. row-gap | column-gap | gap

    • 前两个分别定义行与行的间距、列与列的间距,第三个是前两个的简写
  3. grid-template-areas:用于定义区域,一个区域由单个或多个单元格组成。

    • 用同一个名字表示同一区域的

    • 如果某些区域不需要利用,则使用"点"(.)表示。

    • 注意,区域的命名会影响到网格线。每个区域的起始网格线,会自动命名为区域名-start,终止网格线自动命名为区域名-end

      grid-template-areas: "header header header"
                           "main main sidebar"
                           "footer footer footer";
      
      1
      2
      3
  4. grid-auto-flow:

    • 默认值是row,表示划分网格后,容器的子元素会按“先行后列”的顺序放;
    • 如果设成column,变成“先列后行”;
    • 设为row dense,表示"先行后列",并且尽可能紧密填满,尽量不出现空格。
    • 设为column dense,表示"先列后行",并且尽量填满空格。
  5. justify-items | align-items | place-items

    • 前两个分别定义单元格内容的水平位置(左中右)、单元格内容的垂直位置(上中下),第三个是前两个的简写
    • start | end | center | stretch
  6. justify-content | align-content | place-content

    • 设置的是整个内容区域在容器里面的位置
    • start | end | center | stretch | space-around | space-between | space-evenly
    • space-around:项目之间的间隔比项目与容器边框的间隔大一倍
    • space-evenly:项目与项目的间隔相等,项目与容器边框之间也是同样长度的间隔。
  7. grid-auto-columns | grid-auto-rows

    • 有时候,一些项目的指定位置,在现有网格的外部。比如网格只有3列,但是某一个项目指定在第5行。这时,浏览器会自动生成多余的网格,以便放置项目。

    • 这两个属性用于设置浏览器自动创建的多余网格的列宽和行高。如果不指定这两个属性,浏览器完全根据单元格内容的大小,决定新增网格的列宽和行高。

      .container {
        display: grid;
        grid-template-columns: 100px 100px 100px;
        grid-template-rows: 100px 100px 100px;
        grid-auto-rows: 50px; 
      }
      
      1
      2
      3
      4
      5
      6

      img

  8. grid-template | grid

    • grid-template属性是grid-template-columnsgrid-template-rowsgrid-template-areas这三个属性的合并简写形式。
    • grid属性是grid-template-rowsgrid-template-columnsgrid-template-areasgrid-auto-rowsgrid-auto-columnsgrid-auto-flow这六个属性的合并简写形式。

# 项目属性

  1. grid-column-start | grid-column-end | grid-row-start | grid-row-end

    • 用于指定项目的四个边框,分别定位在哪根网格线。

    • 可以用span关键字,表示跨越多少个网格。

    • 使用这四个属性,如果项目产生了重叠,就使用 z-index 来指定项目的重叠顺序。

      .item-1 {
        grid-column-start: span 2;/* 跨越两列 */
      }
      
      1
      2
      3
  2. grid-column | grid-row

    • 是上面属性的合并简写

      .item-1 {
        grid-column: 1 / 3;
        grid-row: 1 / 2;
      }
      /* 等同于 */
      .item-1 {
        grid-column-start: 1;
        grid-column-end: 3;
        grid-row-start: 1;
        grid-row-end: 2;
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
  3. grid-area:用于指定项目放在哪个区域,或前四种属性的简写,直接指定项目的位置。

  4. justify-self | align-self | place-self

    • 用于设置单元格内容的位置,只作用于单个项目。

优点:代码量少,简洁

缺点:兼容性不是很好,一些浏览器不支持

# 水平垂直居中

其中top、left、right、bottom和margin使用百分比作为值的时候,都是以包含块的宽度为准。

  1. absolute + margin:auto

    • 设置绝对定位,且当水平方向(left/right)或垂直方向(top/bottom)的两个定位属性同时存在的时候,绝对元素在该方向上便具有了流体特性(自动填充),这样宽高固定时,设置 margin:auto; 则 margin 平分剩余空间导致垂直水平居中,适合盒子有宽高的情况
    • 设置了固定 margin 值的元素,宽高 auto 能够自动适应剩余空间
    <div></div>
    <style>
      div{
        position:absolute;
        left: 0; right: 0; top: 0; bottom: 0;
        width: 200px;
        height: 200px;
        margin: auto;
      }
      
      /*或者是*/
      div{
        position:absolute;
        left: 0; right: 0; top: 0; bottom: 0;
        margin: 2rem;
      }
    </style>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
  2. absolute + transform

    • 因为子元素的高度是根据父元素的高度计算的,如果父元素的 height:auto(默认值),则子元素计算得出的高度就是undefined或是null,所以子元素设置 height:100% 无效。
    • 该方法需要考虑浏览器兼容问题
    <div class="outer">
      <div class="inner"></div>
    </div>
    <style>
      html,body{
        height: 100%;
      }
      .outer {
        height: 100%;
        position: relative;
      }
    
      .inner {
        width: 200px;
        height: 200px;
        background-color: skyblue;
        border-radius: 50%;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      }
    </style>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
  3. absolute + margin负值

    • 适合宽高已知的情况
    <div class="outer">
      <div class="inner"></div>
    </div>
    <style>
      html,body{
        height: 100%;
      }
      .outer {
        height: 100%;
        position: relative;
      }
    
      .inner {
        width: 200px;
        height: 200px;
        background-color: skyblue;
        border-radius: 50%;
        position: absolute;
        top: 50%;
        left: 50%;
        margin-top: -100px;
        margin-left: -100px;
      }
    </style>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
  4. flex

    • 要考虑兼容性问题,在移动端使用较多
    .outer {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    1
    2
    3
    4
    5
  5. flex + margin:auto

    .outer {
      display: flex;
    }
    .inner {
      width: 200px;
      height: 200px;
      border-radius: 50%;
      margin: auto;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

# 总结

  1. 父盒子flex,设置justify-content、align-items
  2. 父盒子grid,设置justify-self、align-self
  3. 父盒子flex或grid,子盒子margin:auto
  4. 子盒子设置绝对定位,left:50%、top:50% + transform的translate()
  5. 子盒子设置绝对定位,同时left、right、top、bottom四个属性,margin:auto(固定宽高)
  6. 子盒子设置绝对定位,left:50%、top:50% + margin负值(固定宽高)

# 两栏布局

两栏布局一般指的是左边一栏宽度固定,右边一栏宽度自适应

  1. float+margin-left 或者 float+overflow:auto

    • 右侧宽度默认为 auto,撑满整个父元素
    • 其中使用overflow:auto更方便,这样只要设置 main 盒子的高度为100%,左边盒子就能固定不动;而使用margin-left,左边盒子就会滚上去出现空白
    <style> 
      .aside {
        width: 30vw;
        height: 100vh;
        float: left;
        background: blue;
      }
    
      .main {
        margin-left: 30vw;
        /* 或者换成 overflow: auto,使其成为BFC,利用BFC区域与浮动元素不发生重叠特性 */
      }
    </style>
    <body>
      <div class="aside"></div>
      <div class="main">
        <div class="content"></div>
      </div>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  2. absolute

    • 将父级元素设置为相对定位,左边元素宽度设置为200px,右边元素设置为绝对定位,左边定位为200px,其余方向定位为0。
    <style>
      .outer {
        position: relative;
        height: 100px;
      }
      .left {
        width: 200px;
        background: tomato;
      }
      .right {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 200px;
        background: gold;
      }
    </style>
    <body>
      <div class="outer">
        <div class="left"></div>
        <div class="right"></div>
      </div>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
  3. absolute+margin-left

    • 将父级元素设置为相对定位,左边元素设置为absolute定位,并且宽度设置为200px。将右边元素的margin-left的值设置为200px。
    <style>
      .outer {
        position: relative;
        height: 100px;
      }
      .left {
        width: 200px;
        background: tomato;
      }
      .right {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 200px;
        background: gold;
      }
    </style>
    <body>
      <div class="outer">
        <div class="left"></div>
        <div class="right"></div>
      </div>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
  4. flex

    • 右侧滚动时,左侧也会跟着滚动,因为左侧和右侧高度一样,这是flex的特性(设置grid也一样)
    <style>
      body {
        display: flex;
      }
      .aside {
        width: 25vw;
      }
      .main {
        flex: 1;
        /* 等于flex-grow: 1;宽度自适应*/
      }
    </style>
    <body>
      <div class="aside"></div>
      <div class="main"></div>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

# 总结

  1. float+margin-left:右盒子高度超出100vh,滚动时左盒子会跟着滚动;
  2. float+overflow:auto:设置高度为100vh,左盒子固定,不随右盒子滚动;
  3. absolute:父元素相对定位,右盒子绝对定位;
  4. absolute+margin-left:父盒子相对定位,左盒子绝对定位,右盒子设置margin-left;
  5. flex:父元素设为flex,左盒子固定宽度,右盒子flex为1;
  6. grid:父元素设为grid,及grid-template-columns: 固定宽度 auto

# 三栏布局

三栏布局一般指左右两栏宽度固定,中间自适应的布局。

  1. flex布局

    • 左右两栏设置固定大小,中间一栏flex:1
    <style>
      body {
        display: flex;
      }
      .left {
        width: 30vw;
      }
      .main {
        flex: 1;
      }
      .right {
        width: 20vw;
      }
    </style>
    <body>
      <div class="left"></div>
      <div class="main"></div>
      <div class="right"></div>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  2. 绝对定位

    • 左右两栏绝对定位,中间设置margin
    <style>
    
      .outer {
        position: relative;
        height: 100px;
      }
    
      .left {
        position: absolute;
        width: 100px;
        height: 100px;
        background: tomato;
      }
    
      .right {
        position: absolute;
        top: 0;
        right: 0;
        width: 200px;
        height: 100px;
        background: gold;
      }
    
      .center {
        margin-left: 100px;
        margin-right: 200px;
        height: 100px;
        background: lightgreen;
      }
    </style>
    <div class="outer">
      <div class="left">left</div>
      <div class="center">main</div>
      <div class="right">right</div>
    </div>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
  3. float

    • 左右两栏设置固定大小,并浮动,中间设置margin。注意:中间一栏必须放最后
    <style>
      .outer {
        height: 100px;
      }
    
      .left {
        float: left;
        width: 100px;
        height: 100px;
        background: tomato;
      }
    
      .right {
        float: right;
        width: 200px;
        height: 100px;
        background: gold;
      }
    
      .center {
        height: 100px;
        margin-left: 100px;
        margin-right: 200px;
        background: lightgreen;
      }
    
    </style>
    <div class="outer">
      <div class="left">left</div>
      <div class="center">main</div>
      <div class="right">right</div>
    </div>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
  4. 圣杯布局

    • 利用浮动和负边距来实现。父级元素设置左右的 padding,三列均设置向左浮动,中间一列放在最前面,宽度设置为父级元素宽度的100%,因此后面两列都被挤到了下一行,通过设置 margin 负值将其移动到上一行,再利用相对定位,定位到两边。

    • 注意事项:

      1. 三列浮动,中间列放前面(宽度设为100%),此时左右列被挤到下一行

      2. 父元素设置padding为左右列空出位置;

      3. 左盒子设置margin-left为-100%,挤到上一行(达到以下效果),再使用相对定位,设置负的left达到目的;

      4. 右盒子设置margin-left为负的自身宽度,这样就可以挤到上一行,再使用相对定位,设置负的right即可达到目的。

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title></title>
        <style>
          body {
            margin: 0;
            padding: 0 30vw 0 20vw;
          }
          div{
            float: left;
          }
          #main {
            width: 100%;
            height: 100vh;
            background-color: goldenrod;
          }
          #left {
            width: 20vw;
            height: 100vh;
            background-color: pink;
            position: relative;
            margin-left: -100%;
            left: -20vw;
          }
          #right {
            width: 30vw;
            height: 100vh;
            background-color: skyblue;
            position: relative;
            right: -30vw;
            margin-left: -30vw;
          }
        </style>
      </head>
      <body>
        <div id="main"></div>
        <div id="left"></div>
        <div id="right"></div>
      </body>
    </html>
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
  5. 双飞翼布局

    • 双飞翼布局相对于圣杯布局来说,左右位置的保留是通过中间列的 margin 值来实现的,而不是通过父元素的 padding 来实现的。本质上来说,也是通过浮动和外边距负值来实现的。
    • 因为使用的是中间列margin,则中间列的内容需要另外嵌套一层放内容,中间列设width:100%,中间列的子盒子设margin-left和margin-right
    • 特点:三列浮动,中间列放前面;中间列margin;左右列margin负值定位到目的位置
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title>双飞翼布局</title>
        <style>
          .left,
          .right,
          .main {
            min-height: 200px;
          }
          .left {
            width: 200px;
            background-color: thistle;
          }
          .main {
            background: #999;
          }
          .right {
            width: 300px;
            background-color: violet;
          }
          /* 双飞翼布局重点 */
          .left,
          .main,
          .right {
            float: left;
          }
          .main {
            width: 100%;
          }
          .main-inner {
            margin-left: 200px;
            margin-right: 300px;
          }
          .left {
            margin-left: -100%;
          }
          .right {
            margin-left: -300px;
          }
        </style>
      </head>
      <body>
        <div class="main"><div class="main-inner">中心区</div></div>
        <div class="left">left</div>
        <div class="right">right</div>
      </body>
    </html>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51

# 总结

  1. flex:左右宽度固定,中间一栏flex设为1
  2. grid:设置三个列宽,中间为auto
  3. absolute+margin:父元素相对定位,左右盒子绝对定位,中间盒子设置margin
  4. float+margin:左右盒子设置float,中间盒子放最后,且设置margin
  5. 圣杯:三列浮动,中间列放前面;父元素设置padding;左右列设置margin负值(挤到上一行)+相对定位(定位到目的位置);
  6. 双飞翼:三列浮动,中间列放前面且内容放在子盒子中;中间盒子宽度为100%,其子盒子设置左右margin;左右列通过margin-left负值定位

:为什么不使用float+overflow,通过BFC不与浮动元素重叠的特性来实现三栏布局呢?设置overflow为auto,盒子本身的display还是为block,这样会导致右盒子在下一行向右浮动;而设置中间盒子display为inline-block,需要显式设置宽度为剩余宽度,不然就默认为子元素的宽度,不如float+margin方式的清爽。

# 圣杯布局与双飞翼布局比较

  1. 圣杯布局在DOM结构上显得更加直观和自然
  2. 双飞翼布局省去了很多css,而且由于不用使用定位,可以获得比圣杯布局更小最小宽度
  3. 圣杯使用padding+margin-left+relative;而双飞翼使用margin+margin-left即可

注意:由于双飞翼布局会一直随着浏览器可视区域宽度减小从而不断挤压中间部分宽度。 所以需要设置给页面一个min-width > LeftWidth + RightWidth