前言

感觉css开发中,含金量最高的地方就是布局,把元素摆放在特定的位置;而像设置元素宽高,文本对齐,内外边距,阴影渐变,字体与背景色, 内容溢出显示省略号等这些都是so easy的事情。让容器中的元素垂直水平居中,是日常开发一个高频场景。比面试中问到的BFC/IFC, 如何清除浮动要实用的多(笔者做前端开发的这些年,基本上没有用到过浮动)。因此决定把css实现水平垂直居中方式, 整理一下,以备不时之需。现在我们进入主题。

常见的水平垂直居中方法

为了减少文中的重复代码片段,我们先把每种水平垂直居中对齐的公共样式写一下,其中.base-box.base-item分别是容器和需要对齐元素的外观样式。有读者可能会对下面的css代码产生疑问,说你这种写法是Less/Sass才有的嵌套写法,不是原生css的写法。这里要说明一下,最新版的浏览器已经支持一些原来的Less/Sass才支持的语法,不了解的话请参考此文

.base-box {
  width: 400px;
  height: 200px;
  background-color: greenyellow;
  &+& {
    margin-top: 10px;
  }
  .base-item {
    width: auto;
    height: 100px;
    background-color: red;
  }
}

方案一 flex布局

这种方案可以细分两个版本,一种是在flex容器上设置主轴和交叉轴水平垂直居中属性,另外一种是在flex项目上,设置自身的水平垂直居中属性。

    <link rel="stylesheet" href="./base.css" />
    <style>
      .flex-center-1 {
        display: flex;
        justify-content: center;
        align-items: center;
      }
      .flex-center-2 {
        display: flex;
        justify-content: center;
        .item {
          align-self: center;
        }
      }
    </style>
    <div class="base-box flex-center-1">
      <div class="base-item"></div>
    </div>
    <div class="base-box flex-center-2">
      <div class="base-item item"></div>
    </div>

这种对齐方式应用最广, 因为对子元素的宽高没有限制,而且浏览器兼容性也比较好。

效果如下:

CSS水平垂直居中方法这么多,总有一款适合你

方案二 绝对/相对定位+transform方案

先将子元素设置为相对或绝对定位,再将子元素的位置设定为父容器的中心点(即top: 50%left: 50%),这会使子元素的左上角相对于父容器的中心进行定位。

CSS水平垂直居中方法这么多,总有一款适合你

为什么将子元素设置成相对或绝对定位,再设置top: 50%left: 50%,都能让子元素的左上角处于父容器的中心点。这是因为采用相对定位时,top和left设置为百分比,这个百分比是相对于父元素的宽高而言的。采用绝对定位时,由于给父元素设置了相对定位,这是子元素的top和left设置的百分比,也是根据父元素的宽高计算的。如下图所示:

CSS水平垂直居中方法这么多,总有一款适合你

所以这两种情形,都能使用同样的移动效果。接下来通过transform: translate(-50%, -50%)将子元素向左和向上平移自身宽高的一半(translate中的百分比是根据自身的宽高来计算的),这样就能使子元素的中心点与父容器的中心点重合,从而实现水平和垂直居中。

    <link rel="stylesheet" href="./base.css" />
    <style>
      .transform-center-1 {
        position: relative;
        .item-1 {
          position: absolute;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
        }
      }
      .transform-center-2 {
        .item-2 {
          position: relative;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
        }
      }
    </style>
    <div class="base-box transform-center-1">
      <div class="base-item item-1"></div>
    </div>
    <div class="base-box transform-center-2">
      <div class="base-item item-2"></div>
    </div>

这种方式不需要预先知道子元素的具体尺寸,因此适用于未知宽高或者动态变化尺寸的元素居中问题。同时,由于transform不会影响布局流(不影响其他元素的位置),所以是一种性能较好的居中解决方案

方案三 table-cell方案

当一个元素的display属性被设置为table-cell时,该元素将会表现出如同<td>单元格一样的行为特征,设置vertical-align: middle在具有display: table-cell的元素上,可以使该元素内的内容在垂直方向上居中对齐。水平居中,可以使用给父元素设置text-align:center属性, 子元素设置display:inline-block; 。在CSS中,给父元素添加text-align: center;属性意味着父元素内部的行内内容或行内级元素会被水平居中对齐。当子元素设置了display: inline-block;属性 ,子元素会变成行内块元素,它遵循行内元素的对齐规则,所以当父元素设置了text-align: center;时,子元素就会按照水平居中规则显示。

    <link rel="stylesheet" href="./base.css" />
    <style>   
     .table-cell-box {
        display: table-cell;
        text-align:center;
        vertical-align: middle;
        .item {
          display: inline-block;
        }
      }
    </style>
    <div class="base-box table-cell-box">
      <div class="base-item item"></div>
    </div>

通过这样的方式,即使子元素内容大小未知,也可以实现内容在容器内的灵活居中对齐。这种方法尤其适用于兼容性要求较高的场景,在 Flexbox 或 Grid 布局还未普及之前的浏览器环境下是一种常用的居中解决方案。

方案四 table布局

这种方案很好理解,设置单元格中内容垂直居中,子元素就会表现为垂直居中,接着设置单元格内容水平居中,将子元素设为内联块元素,子元素在水平方向上就会居中。这样就实现了垂直水平居中。此种方案的缺点显而易见,dom层级过多,但优点是不需要关注子元素的尺寸。

    <link rel="stylesheet" href="./base.css" />
    <style>
      .table-box {
        vertical-align: middle;
        text-align: center;
        .item {
          display: inline-block;
        }
      }
    </style>
    <table>
      <tr>
        <td class="base-box table-box">
          <div class="base-item item"></div>
        </td>
      </tr>
    </table>

方案五 Grid布局

CSS Grid布局是一种现代布局模式,它提供了一套强大的二维布局系统,使得在网页设计中更容易实现元素的复杂排列,包括轻松地实现容器内单个或多个项目的水平和垂直居中。这里的.grid-box-1是需要进行居中布局的容器,.item是需要居中的子元素。通过display: grid将容器转换为网格容器,而align-items: centerjustify-items: center两个属性会让容器内的所有直接子元素都将自动地沿行轴(即水平方向)和列轴(即垂直方向)居中对齐。还有一种方式就是单独给子元素设置水平和垂直居中。

    <link rel="stylesheet" href="./base.css" />
    <style>
      .grid-box-1 {
        display: grid;
        align-items: center;   /* 沿垂直方向(列轴)居中 */
        justify-items: center; /* 沿水平方向(行轴)居中 */
      }
      .grid-box-2 {
        display: grid;
        .item {
          justify-self: center; /* 单独控制水平居中 */
          align-self: center; /* 单独控制垂直居中 */
        }
      }
    </style>
    <div class="base-box grid-box-1">
      <div class="base-item item"></div>
    </div>
    <div class="base-box grid-box-2">
      <div class="base-item item"></div>
    </div>

grid布局和flex布局十分相像,无论子元素尺寸如何变化,都能让其保持在容器中心位置。但移动端浏览器对Grid布局的支持性不如flex布局那么好。

方案六 writing-mode

CSS的writing-mode属性主要用于改变文本的方向和布局流,可以与其它CSS属性组合使用,间接实现垂直居中效果。当父元素writing-mode设为垂直方向时,原本的水平方向属性(如text-align)将会影响子元素垂直方向的对齐。看起来的效果是子元素在父容器中垂直居中。接着在子元素中在放置一个后代元素.content,设置这个content为内联元素,在子元素中通过文本居中属性,让后代元素水平居中。从而实现后代元素水平垂直的效果。这种方式实现水平居中不太优雅,需要额外添加后代元素,层级较多,好处是可以不必知道后代元素的尺寸,让后代元素居中。

    <link rel="stylesheet" href="./base.css" />
    <style>
      .writing-mode-box {
        /* 竖排从右到左 */
        writing-mode: vertical-lr;
        text-align: center;
        .item {
          display: inline-block;
          /* 默认的从左到右横排 */
          writing-mode: horizontal-tb;
          text-align: center;
          height: auto;
          width: 100%;
          .content {
            display: inline-block;
            text-align: left;
            background-color: green;
          }
        }
      }
    </style>
     <div class="base-box writing-mode-box">
      <div class="base-item item">
        <div class="content">1111</div>
      </div>
    </div>

CSS水平垂直居中方法这么多,总有一款适合你

方案七 绝对定位+margin:auto

父元素设置相对定位,子元素设置绝对定位,同时设置子元素的top,bottom,left,right位置偏移为0。这使得子元素的边缘与父元素四个方向的边缘相接触,形成一个覆盖整个父元素的盒子。当子元素设置了四个边缘位置偏移为0之后,其margin属性中的auto值就有了可以参考的上下文——即元素自身的尺寸与父元素之间的差距。在水平方向上,margin-leftmargin-right同时设为auto时,浏览器会平均分配元素宽度之外的剩余空间,从而使元素水平居中。在垂直方向上,若元素具有确定高度且同时设置了topbottom0margin-topmargin-bottomauto的话,根据CSS规范,上下边距值会被设置为相同的正值,能够将子元素推至上下边缘正中间,从而实现垂直居中。

    <style>
     .absolute-margin-auto {
       position: relative;
       .item {
         position: absolute;
         left: 0;
         top: 0;
         bottom: 0;
         right: 0;
         margin: auto;
       }
     }
   </style>
   <div class="base-box absolute-margin-auto">
     <div class="base-item item"></div>
   </div>

这种对齐方式需要给子元素设置固定的宽高。因为margin: auto在水平或垂直方向上的居中对齐要求子元素的宽高不能为auto。适用范围不如前面三种居中对齐方式那么广泛。

方案八 绝对定位+负margin

此种方式和transform方式比较相似。不同的是transform方式是让浏览器自动计算水平和垂直方向上的具体偏移值,这种方式要自己计算水平垂直方向上的偏移值。 此外需要知道父容器的宽和高,自身也需要设置固定的宽高,使用起来限制比较大。

    <link rel="stylesheet" href="./base.css" />
    <style>
      .absolute-negative-margin {
        position: relative;
        .item {
          position: absolute;
          left: 50%;
          top: 50%;
          margin-left: -100px;
          margin-top: -50px;
        }
      }
    </style>
    <div class="base-box absolute-negative-margin">
      <div class="base-item item"></div>
    </div>

方案九 绝对定位+calc

当知道容器的宽度和高度时,可以通过父相子绝定位,结合calc()计算出元素的正确偏移量来实现居中。这种方式是把绝对定位+负margin布局方式偏移设置两步合并成一步,此种方式需要知道子元素的尺寸,以及给父元素设置固定尺寸,使用起来有一定的限制性。

    <link rel="stylesheet" href="./base.css" />
    <style>
      .absolute-calc-box {
        position: relative;
        .item {
          position: absolute;
          left: calc(50% - 100px); /* 减去的宽度为子元素自身宽度的一半 */
          top: calc(50% - 50px);   /* 减去的高度为子元素自身高度的一半 */
        }
      }
    </style>
    <div class="base-box absolute-calc-box">
      <div class="base-item item"></div>
    </div>

方案十 line-height方案

在CSS中,当父元素设置了固定的高度(height)和行高(line-height),并且这两个值相等时,就可以实现单行文本内容的垂直居中对齐。将子元素设置为内联块级元素。子元素的表现效果和单行文本一样,再给父容器设置文本水平居中,子元素就能水平居中。这种方法的局限性比较大,需要知道父元素的高度,还有只对单行内容有效。

    <link rel="stylesheet" href="./base.css" />
    <style>
      .single-box {
        line-height: 200px;
        text-align: center;
        .item {
          display: inline-block;
          /* 显式设置子元素的行高与自身高度相等 */
          line-height: 100px;
        }
      }
    </style>
    <div class="base-box single-box">
      <div class="base-item item">11</div>
    </div>

CSS水平垂直居中方法这么多,总有一款适合你

方案十一 伪元素方案

当有多个行内块级元素(display: inline-block),并且每个元素都设置了vertical-align: middle时,所有元素的垂直对齐是相对于它们所在行框(line box)的基线(baseline)进行的 ,如果各个元素的高度不同,它们各自的“中间”位置可能会有所差异,但都会尽量接近于行框的垂直中点,这个中点是基于行框内所有字体的 (x 字母高度)平均计算得出的。在实际应用中,通过在父元素中创建一个伪元素,并设置其height: 100%display: inline-block,这样它会占据父元素的整个高度。接着给每个子元素设置vertical-align: middle,然后就可以通过vertical-align: middle将实际内容元素与这个伪元素垂直对齐,从而达到父容器内各个子元素垂直居中的效果。水平居中比较简单。这种方案可以实现单行多个不同高度的垂直居中。不需要知道每个子元素的尺寸。缺点就是需要额外创建一个伪元素,不如其它对齐方案优雅。

    <link rel="stylesheet" href="./base.css" />
    <style>
      .single-box {
        text-align: center;
        &::before {
          content: "";
          width: 0;
          height: 100%;         
          display: inline-block;
          vertical-align: middle;
        }
        .item {
          display: inline-block;
          width: 50px;
          vertical-align: middle;
          &:nth-of-type(1) {
            height: 40px;
          }
          &:nth-of-type(2) {
            height: 100px;
          }
          &:nth-of-type(3) {
            height: 80px;
          }
        }
      }
    </style>
   <div class="base-box pseudo-box">
      <div class="base-item item">11</div>
      <div class="base-item item">22</div>
      <div class="base-item item">33</div>
   </div>

CSS水平垂直居中方法这么多,总有一款适合你

怎么选?

学习了这11种水平垂直居中方案,使用的时候该怎么选呢。从通用性,易用性,兼容性角度来讲,优先要考虑对子元素宽高没有限制的对齐方式,可按如下顺序选择flex > 相对/绝对定位+transform > table-cell > grid,write-modetable这两种方案不推荐使用,一个太偏门,一个不优雅。 中策可供选择的对齐方案是绝对定位+margin:auto > 绝对定位+calc > 绝对定位+负marginline-height伪元素对齐方案,可以作为单行内联块级元素对齐的备选方案。

最后

掌握了文章的水平垂直居中方案,如果你仅想单独实现水平或垂直居中效果,可以把这11个示例中的另一半用不到的居中内容移除。你们没有时间整理这些高频使用的知识点,我来帮你们整理,你只需收藏一下,需要用的时候直接复制示例中的代码片段。每个示例我都验证过效果了,经得起实践的考验。我所知道的css水平垂直居中对齐方案就是这11种,如果你还知道别的方案,欢迎补充。我们留言区见。