CSS之垂直水平居中的背后

时间:2022-09-14 19:55:47

  最开始,我想说,这个体系有点大,我写的并不好。就当作是一个思路吧,虽然这个思路有点乱。几乎每一个实现方案的背后都是该属性及其组合的原理,每一个都要剖析其规范细节的话,这篇文章绝不会是这样的篇幅,所以每当需要说更多的时候我都简单阐述后一笔带过,然后附上参考资料。后面若是写css系列的时候再详细讲吧。

  其实这道面试题,真的考察很多东西,个人理解它属于开放类型的问题,没有指定范围的答案,所以它就可以涉及到很大范围的知识概念,并且以此判断开发者对技术理解的深度和宽度。那么要解决这个问题,最大的难点在于分类。首先,从题目上来说,可以分为垂直居中、水平居中,子元素确定宽高下的水平居中、子元素确定宽高下的垂直居中,甚至于父元素确定宽高、不确定宽高,父元素子元素都确定宽高,都不确定宽高等等情况。其次,从技术解决方案的角度上来说,又可以有比如Flex、比如Margin、比如Position等。再者,基于不同的技术手段,其实还要区分display的属性值,也就是盒子的类型。

  所以在解决问题的切入点上就很难区分要以什么样的角度去分类,从而作为后面解题的基础,本菜鸡就以知识点也就是css属性为切入点来分类,针对不同场景的父子盒子的垂直水平提出解决方案。

  首先,我们先写一个demo代码,我们后面所有的垂直水平居中解决方案的基础代码环境都是基于这个demo的。完整代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      #father {
        box-sizing: border-box;
        width: 200px;
        height: 200px;
        border: 1px solid;
      }
      #child {
        box-sizing: border-box;
        width: 50px;
        height: 50px;
        border: 1px solid;
        background: red;
      }
    </style>
  </head>
  <body>
    <button >切换父盒子宽高</button>
    <button >切换子盒子宽高</button>
    <div >
      <div ></div>
    </div>
  </body>
  <script>
    let father = document.getElementById("father");
    let child = document.getElementById("child");
    let fatherBtn = document.getElementById("fatherBtn");
    let childBtn = document.getElementById("childBtn");
    fatherBtn.addEventListener("click", function () {
      father.style.width = father.offsetWidth + 30 + "px";
      father.style.height = father.offsetHeight + 30 + "px";
    });
    childBtn.addEventListener("click", function () {
      child.style.width = child.offsetWidth + 15 + "px";
      child.style.height = child.offsetHeight + 15 + "px";
    });
  </script>
</html>

  就很简单,页面看起来就是这个样子:

CSS之垂直水平居中的背后 

  点击对应的按钮,可以分别增加父子盒子的宽高。demo代码就是这样,so easy,下面就开始我们的水平垂直居中解决方案之路。

  这一部分,我只提供某一个CSS属性所提供的独立的能力,比如它可以实现垂直居中,或者水平居中,或者可以实现垂直水平居中。让我们深入理解单独属性的能力。

一、Grid

  网格布局,它可以将页面划分成一个个可以任意组合的网格,以前这样的处理只能通过复杂的css框架达到预期的效果。现在,浏览器内置了这样的能力。Grid布局与Flex布局有一定的相似性,都可以指定容器内部多个项目的位置。但是,它们也存在重大区别。

  Flex 布局是轴线布局,只能指定"项目"针对轴线的位置,可以看作是一维布局。Grid 布局则是将容器划分成"行"和"列",产生单元格,然后指定"项目所在"的单元格,可以看作是二维布局。Grid 布局远比 Flex 布局强大。

  采用网格布局的区域,称为"容器"(container)。容器内部采用网格定位的子元素,称为"项目"(item)。你看,Grid也有类似于Flex的定义。但是Grid的针对容器的划分要比Flex复杂得多。

  Grid容器中的水平区域称为行,垂直区域称为列,行与列的交叉区域叫做单元格。诶?这不是跟表格的命名很像?嗯~~几乎一模一样。

  划分网格的线,称为"网格线"(grid line)。水平网格线划分出行,垂直网格线划分出列。正常情况下,n行有n + 1根水平网格线,m列有m + 1根垂直网格线,比如三行就有四根水平网格线。

  上面那段话也是我复制下来的,详细的内容就在末尾的参考资料里,有兴趣可以深入的学习。现代浏览器的支持情况还是可以的。

  那么,针对本篇的问题点,基于Grid要如何实现垂直水平居中:

#father {
  box-sizing: border-box;
  width: 200px;
  height: 200px;
  border: 1px solid;
  display: grid;
  justify-content: center;
  align-content: center;
}

  代码也十分简单。

  这一小节我们所贴上的代码十分的少,甚至没有过多说明grid的各种属性及其能力。我们要注意的是,在使用grid的情况下,实际上我们已经更改了盒子的display值,也就是使用grid的盒子已经不再是单纯的block了。

  grid可以实现水平居中并且响应性的无论是inline还是block的本质是因为它是grid,不是inline也不是block。

二、Flex

  Flex布局是W3C在2009年提出的一种新的布局解决方案,可以简便、完整、响应式的实现各种页面布局。目前它已经得到了所有浏览器的支持,这意味着现在就可以很安全的使用这项功能。好吧,我相信大家都已经使用Flex技术使用的非常顺手了。具体的细节内容大家可以参考本篇末尾的参考资料部分。

  我们来了解下Flex布局下的基本技术概念。采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。基于此,其中一部分css属性是针对容器的,比如flex-direction、justify-content、align-items等。还有一部分属性是针对项目的,比如flex-grow、flex-shrink、flex- basis等。

  然后,我们要注意的一点就是,容器默认存在两根轴,水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;交叉轴的开始位置叫做cross start,结束位置叫做cross end

  项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size

  其实上面大部分的内容都来自于参考资料,其实基于Flex的水平垂直居中的解决方案十分简单,你肯定也知道,就是这样:

#father {
  box-sizing: border-box;
  width: 200px;
  height: 200px;
  border: 1px solid;
  display: flex;
  justify-content: center;
  align-items: center;
}

  我只加了三行代码,就可以实现我们理想中的垂直水平居中,非常简单、快捷、舒适。

  你看,解决方案就这么简单。另外,我尤其要强调的一点是,Flex布局,已经不再是单纯的盒模型,它是Flex Container,即弹性容器。我们通过设置display属性为flex,改变了盒子的类型。这个大家尤其要注意点。

  最后,大家可以看到,针对垂直水平居中的Flex和Grid的解决方案都比较简单,但是,它们的详细内容及核心要点我都没写,都在文末的参考资料中,那里才是你要关注的重点,我仅仅提供了解决方案的一种思路,说得多了,真的觉得没意义。一定要去看文末的参考资料去学习其基本原理。嗯~~参考资料才是本篇的重点。

  那么接下来,我们继续看看其他解决方案。

三、Transform

  不知道大家对这个东西熟悉不熟悉,CSS transform 属性允许你旋转,缩放,倾斜或平移给定元素。注意给定元素这个词,也就是说,transform属性,是针对自身来进行变换的。那我们思考一下,针对自身,也就是说我无法知道父盒子的一些信息,比如宽高啊啥啥的。所以,transform能做到的,只是这样:

#child {
  box-sizing: border-box;
  width: 50px;
  height: 50px;
  border: 1px solid;
  background: red;
  translate: 75px 75px;
}

  注意,这回我们是改的子元素了噢。上面的代码~~诶~~~诶?!不是transform么?你咋可以直接写translate?CSS 属性 translate 允许你单独声明平移变换,并独立于 transform 属性。嗯~就是translate可以单独写,跟transform一样:

#child {
  box-sizing: border-box;
  width: 50px;
  height: 50px;
  border: 1px solid;
  background: red;
  transform: translate(75px, 75px);
}

  展示的结果,如我们所料:

CSS之垂直水平居中的背后

  那么文字是不是也可以呢?我们修改下demo:

<body>
  <div class="btn-area">
    <button id="fatherBtn">切换父盒子宽高</button>
    <button id="childBtn">切换子盒子宽高</button>
  </div>
  <div id="father">
    <div id="child">我是文字我</div>
  </div>
</body>

  我们加点字,然后改下css:

#child {
  box-sizing: border-box;
  width: 50px;
  height: 50px;
  border: 1px solid;
  background: red;
  transform: translate(75px, 75px);
  display: inline;
}

  直接改成inline,你会发现:

CSS之垂直水平居中的背后

  不行。。。inline不行。但是inline-block却可以。额~~这个说起来有点复杂,简单说的话就是规范的定义就是这样的,设置了transform属性的元素叫做可转换元素。然后,我找了一个小时找到了这句话:

all elements whose layout is governed by the CSS box model except for non-replaced inline boxes, table-column boxes, and table-column-group boxes

   啥子意思呢,就是盒子类型除了是inline、table-column以及table-column-group的都可以使用transform。不信你试试呢?我肯定不会一个一个去试的,但是我确实试了几个,好像确实是这样。

  如果只是单纯的translate,只能移动自身,只能在父子元素都是固定宽高的情况下实现垂直水平居中,或者说,translate本身就与其它内容无关,只与自己有关,因为是自身的transform,一旦父子元素的宽高变化,它肯定就无法垂直水平居中了。

  还有个问题,为什么是75px?这个75px是怎么计算出来的?就是父盒子width/2 - 子盒子width/2。其实就是一半减一半啦~~

  那我用百分比不行么?不行,因为你不知道父盒子的宽高,而如果一定要只使用translate,只能在盒子变化的时候通过js来计算父子盒子的宽高,从而重新设置子元素的translate基于自身移动的X轴与Y轴的值。不信我们试一下:

#child {
  box-sizing: border-box;
  width: 50px;
  height: 50px;
  border: 1px solid;
  background: red;
  transform: translate(100%, 100%);
}

  效果是这样的:

CSS之垂直水平居中的背后 

   诶?transform只跟自身有关系,所以它translate的100%其实是50px,那我不是可以这样:

#child {
  box-sizing: border-box;
  width: 50px;
  height: 50px;
  border: 1px solid;
  background: red;
  transform: translate(150%, 150%);
}

  嗯……好像没毛病,但是你这样就是强硬计算的。跟75px没区别。但是我们仍旧强调了一点就是,transform只与自身有关系transform只与自身有关系!transform只与自身有关系!transform只与自身有关系!重要的事情说了五六遍了。你记住了吧?

  其他的可能,就是不可能。额……在只有translate的前提下。

  OK,translate告一段落。

四、Position

  position属性想必大家都很熟悉了,相对定位、绝对定位、粘性定位、固定定位啊,啊不,没有等等,啊不不不,还有等等,不重要了~

  position定位的位移计算方式与父子盒子无关,不同的定位方式,仅影响它相对计算的方式。具体的内容细节我不多说,去文档看吧。我要说的是,position的位移依赖于top、right、bottom、left属性。

  position适用于:

all elements except table-column-group and table-column

  也就是除了table-column-group和table-column的所有display属性值。和transform的区别就是inline也可以使用position

一)相对定位(Relative,Sticky)

1、relative

  我们来看下代码:

#child {
  box-sizing: border-box;
  width: 50px;
  height: 50px;
  border: 1px solid;
  background: red;
  position: relative;
  top: 75px;
  left: 75px;
}

  效果是这样的:

CSS之垂直水平居中的背后 

   看起来还可以,跟translate的效果是一样的,但是实际上这样做的问题很多,并不适用大多数的实际应用场景,有很大的局限性。当我们切换父子盒子的宽高的时候,只要父盒子的一半减去子盒子的一半等于你设置的宽高,那么就可以实现水平垂直居中的效果。

  那么百分比呢?其实百分比也跟translate是类似的。我就不再写了。要注意的是,relative是相对于自己所在位置进行相对位移的。所以,在本例的代码中展现的效果和translate十分类似。

2、sticky   

  粘性定位,实际上要依赖于滚动盒子。sticky的计算方式依赖于最近的滚动祖先。注意,一个 sticky 元素会“固定”在离它最近的一个拥有“滚动机制”的祖先上(当该祖先的overflow 是 hiddenscrollauto, 或 overlay时),即便这个祖先不是最近的真实可滚动祖先。

#father {
  box-sizing: border-box;
  width: 200px;
  height: 200px;
  border: 1px solid;
  overflow: scroll;
}
#child {
  box-sizing: border-box;
  width: 50px;
  height: 50px;
  border: 1px solid;
  background: red;
  position: sticky;
  top: 75px;
  left: 75px;
}

  但是这个例子由于父盒子没有被撑开滚动,所以不太能看出来滚动的效果,我们改下例子:

<div id="father">
  我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我
  <div id="child">我是文字我</div>
  是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字我是符合子的文字
</div>

  不用修改css,只是让父盒子可以被文字撑开即可:

CSS之垂直水平居中的背后

   我们就可以看到这样的效果,它的本体跟relative一样,也没有脱离文档流。所以relative和sticky其实都是相对定位

二)绝对定位(Absolute、Fixed)

  绝对定位和相对定位有一个本质的区别,就是绝对定位会脱离文档流,形成一个独立的图层。由于绝对定位可以脱离文档流,与正常DOM元素的排版互不影响,所以绝对定位要比相对定位的使用场景更加广泛。

1、absolute

  在垂直水平居中的场景下,absolute要比relative更加的有意义。因为大多数的后面的关于不定宽高的垂直水平问题的讨论,论点,论据,证明等等几乎都离不开absolute。我们来看下代码:

#father {
  box-sizing: border-box;
  width: 200px;
  height: 200px;
  border: 1px solid;
  position: relative;
}
#child {
  box-sizing: border-box;
  width: 50px;
  height: 50px;
  border: 1px solid;
  background: red;
  position: absolute;
  top: 75px;
  left: 75px;
}

  这样做,跟上一小节的relative部分没有区别。一个意思。嗯~~没了?没了……。单纯的absolute只能做到这样,跟relative一样。

2、fixed

  fixed和absolute的区别只是定位计算的相对元素不同,absolute是相对于最近的具有定位属性的祖先元素,而fixed则直接相对于浏览器的视口来计算定位。例子~~嗯~~没有例子。

五、Margin

  额~margin我想大家都知道它能干什么了,盒模型的外边距。可以通过设置margin:0 auto;从而让该元素水平居中。

#child {
  box-sizing: border-box;
  width: 50px;
  height: 50px;
  border: 1px solid;
  background: red;
  margin: 0 auto;
}

  看起来就像这样:

CSS之垂直水平居中的背后

  其实,我们这样写也行:

margin: 75px;

  就垂直水平居中了:

CSS之垂直水平居中的背后

  那么还要强调一点的是,margin和padding的百分比计算参照是父盒子的宽度。无论margin和padding设置的是上下左右哪一个,它百分比计算的参照都是父盒子的宽度

六、Padding

  如果我想用padding设置垂直水平居中,你猜是否可以?

#father {
  box-sizing: border-box;
  width: 200px;
  height: 200px;
  border: 1px solid;
  padding: 75px;
}

   效果是这样的:

CSS之垂直水平居中的背后

   嗯,其实这个原理跟之前相对于父元素移动的transform和position类似。但是也仅仅如此了。

  那记不记得我在margin那部分说过的一句话,假如我这样设置:

padding:10%

  这个10%是多少?嗯。。我也不知道,你当前设置padding属性的父盒子的宽度的10%。

七、Line-height

  利用line-height,我们可以这样:

#father {
  box-sizing: border-box;
  width: 200px;
  height: 200px;
  border: 1px solid;
  line-height: 200px;
}
#child {
  box-sizing: border-box;
  width: 50px;
  height: 50px;
  border: 1px solid;
  background: red;
  display: inline-block;
}

  效果是这样的:

CSS之垂直水平居中的背后

   诶?诶诶饿诶?你这不太对啊,我这膀胱一扫你这子元素就没垂直居中啊。那我改一下:

#father {
  box-sizing: border-box;
  width: 200px;
  height: 200px;
  border: 1px solid;
  line-height: 225px;
}

  诶?好像可以了:

CSS之垂直水平居中的背后

   你说这是为啥,200px咋就不对劲呢?嗯~~因为基线问题。但是line-height确实可以让我们的行内块元素垂直居中。

八、Text-align

The text-align CSS property sets the horizontal alignment of the inline-level content inside a block element or table-cell box. This means it works like vertical-align but in the horizontal direction.

  就是说其实text-align跟vertical-align类似,只不过text-align是水平,vertical-align是垂直。

  利用text-align,我们可以这样:

#father {
  box-sizing: border-box;
  width: 200px;
  height: 200px;
  border: 1px solid;
  text-align: center;
}
#child {
  box-sizing: border-box;
  width: 50px;
  height: 50px;
  border: 1px solid;
  background: red;
  display: inline-block;
}

  嗯。。好像没啥问题:

CSS之垂直水平居中的背后

九、Vertical-align

  vertical-align是用来确定行内元素或者表格单元格的垂直对齐方式的。它可以使行内元素垂直居中。我们以Table为例来讲解下vertical-align。

  table这个东西其实有点奇怪并且很少使用,但是它也确实能实现垂直水平居中。利用table,一种是修改DOM,写个table的结构:

<table>
  <tbody>
    <tr>
      <td class="father">
        <div class="child"></div>
      </td>
    </tr>
  </tbody>
</table>

  CSS是这样的:

.father {
  box-sizing: border-box;
  width: 200px;
  height: 200px;
  border: 1px solid;
}
.child {
  box-sizing: border-box;
  width: 50px;
  height: 50px;
  border: 1px solid;
  background: red;
}

  我们没有给css添加任何除了宽高背景外的属性。

  效果是这样的:

CSS之垂直水平居中的背后

  我们审查下DOM,可以看到:

CSS之垂直水平居中的背后 

   tbody的vertical-align是middle,而td则是这样:

CSS之垂直水平居中的背后

   它一级一级的继承下来了。不信大家可以亲自试一试

   确实垂直居中了,因为table-cell天然垂直居中,当然,它垂直居中的原因则是因为table的DOM会默认赋予一些css属性,比如vertical-align。另外一种就是不用修改DOM,而是修改css属性:

#father {
  box-sizing: border-box;
  width: 200px;
  height: 200px;
  border: 1px solid;
  display: table-cell;
  vertical-align: middle;
}

  效果是一样的:

CSS之垂直水平居中的背后

   到最后我们发现,垂直居中的根本原因并不是table,而是vertical-align。

十、writing-mode

  这个东西,好像大家都不怎么常用。但是,它确实提供了一种垂直水平居中的解法。当然,它本身无法实现垂直水平居中,它只是改变了文档流的流向。writing-mode定义了文本水平或垂直排布以及在块级元素中文本的行进方向。writing-mode这个东西其实理解起来也不算复杂,只不过它的关键字有点烦人。writing-mode目前有五个属性:horizontal-tb、vertical-rl、vertical-lr以及浏览器支持情况一点都不好的试验性属性sideways-rl和sideways-lr。具体属性的含义,请看文末的MDN链接或自行学习。

  我们来写个例子试一下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .father-mode {
        width: 100px;
        border: 1px solid;
      }
    </style>
  </head>
  <body>
    <div class="father-mode">
      我是文字编辑,今年390岁,喜欢吃美食,喜欢看美女,喜欢游美景。
    </div>
  </body>
</html>

  效果就是这样的:

CSS之垂直水平居中的背后

   就很正常。

然后我们加点代码:

.father-mode {
  width: 100px;
  border: 1px solid;
  writing-mode: vertical-lr;
}

  CSS之垂直水平居中的背后

   有点长,嗯~~换句话说,利用writing-mode可以改变文档流的流向,嗯,大家记住这句话就可以了。

第二部分 组合

  上一个部分,我们花了不小的篇幅去整理一些在垂直水平居中问题上可以用到的css属性,我都是单独拎出来简单说明的。这部分内容也并不简单,我并没有一个属性一个属性完全的抛开了揉碎了去讲,只是在限定的条件下展示了某一个属性可以居中的能力。当然,还提了一些百分比的相对计算方式,也就是css单位的计算方式,css单位也是一个很复杂的体系,大家要详细的去了解学习。

  我们简单总结下第一部分的内容。大概可以这样来分:

  1. 布局:Flex、Grid
  2. 盒模型:Margin、Padding
  3. 位移:Translate、Position
  4. 行内对齐:Text-align、Vertical-align、Line-height
  5. 流:Writing-mode
  6. 计算单位。

  我特意加上了计算单位,因为它真的很重要,很多面试的时候,也会问这个问题。但是就计算单位再加上对应可以使用该单位的属性来说,完全可以再写一篇博客了。所以我可能后面会写关于单位的博客。这里肯定就不多说了。

  那么接下来,我们就使用以上的内容,组合起来,实现不定宽高垂直水平居中。

  在实现之前,我们想想,我们大概会用到哪些属性?熟悉flex和grid肯定是不会用的,因为它们自成一套布局体系。只要使用这个体系,就可以自动的响应式的实现居中效果。接下来就剩下盒模型、位移、行内对齐以及流,再配合计算单位,来实现对应display值的垂直水平居中。

  注意,我们下面实现的前提是不定宽高的父子元素的垂直水平居中噢。

一、absolute + transform

  这种解决方案是最好理解的方式。因为它牵扯到的属性及属性值都比较表面,就是书写的那种意思。我们来看下:

#father {
  box-sizing: border-box;
  width: 200px;
  height: 200px;
  border: 1px solid;
  position: relative;
}
#child {
  box-sizing: border-box;
  width: 50px;
  height: 50px;
  border: 1px solid;
  background: red;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

  其实这段代码就涉及到了两个点,absolute位移的计算方式,translate位移的计算方式。由于absolute的相对计算位置是最近定位的祖先元素,并且是从左上角的原点开始计算,所以当position位移上、左各50%的情况下,子元素从左上角计算移动了50%。就是这样:

CSS之垂直水平居中的背后

 

 

   所以,我们就需要让自身再往左往上移动自身的一半距离,所以,我们利用translate是移动自身的特性,通过百分比单位,移动自身的一半距离。

CSS之垂直水平居中的背后

 

 

   就实现了我们想要的结果。这种方式可以自适应任何宽高的父子元素。你可以试试~,至于原理,就是利用百分比单位来得到父子元素的宽高,最后利用相对计算方式不同的position和translate来进行移动。

  嗯~这是第二部分最好理解的一种方式了。继续~~

二、absolute + margin: auto

  这种方式我们要学的东西就稍微多了点,我们先看解决方案:

#father {
  box-sizing: border-box;
  height: 200px;
  width: 200px;
  border: 1px solid;
  position: relative;
}
#child {
  box-sizing: border-box;
  border: 1px solid;
  background: red;
  position: absolute;
  width: 50px;
  height: 50px;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  margin: auto;
}

  这种解决方案看起来还不错。

CSS之垂直水平居中的背后

  但是~~我们后面再说但是。

  我们先看首先absolute的上下左右距离都是0,还设置了margin为auto。按理来说,如果当前子盒子没有宽高,的话你的absolute的距离都设置为0,那么应该是会撑满父元素的:

#child {
  box-sizing: border-box;
  border: 1px solid;
  background: red;
  position: absolute;
-  width: 50px;
-  height: 50px;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
}

  效果就是这样的:

CSS之垂直水平居中的背后

 

 

   这就意味着,当前的方案子元素必须是有宽高的。加上宽高的话,就变成这样了:

CSS之垂直水平居中的背后

 

 

   好像跟没加一样,嗯……好像是。但是一旦加上margin:auto。就可以垂直水平居中了,为啥不是margin: 0 auto;。必须是margin: auto;呢。auto的背后有复杂的计算方式,仅在当前的场景下,我们给子元素设置margin:auto;它会自动计算剩余空间,然后平分,这也是为什么子元素必须有宽高的原因。

三、line-height + text-align + vertical-align

  嗯,不建议大家使用这种方式对布局实现垂直水平居中,你可以针对文字使用,因为这几个属性的本身设计就是如此,不要为赋新词强说愁,所以,下面的例子,仅作为方案:

#father {
  box-sizing: border-box;
  height: 200px;
  width: 200px;
  border: 1px solid;
  line-height: 200px;
  text-align: center;
  font-size: 0px;
}
#child {
  box-sizing: border-box;
  border: 1px solid;
  background: red;
  width: 50px;
  height: 50px;
  font-size: 16px;
  display: inline-block;
  vertical-align: middle;
  line-height: initial;
  text-align: left;
}

  这种方案,因为涉及到line-height,所以可以想想到,父盒子的宽高一定是确定的。我们可以去掉子元素的50px宽高试一下:

CSS之垂直水平居中的背后

 

 

   嗯,就是inline-block的表现。我们分析下每个属性在父子元素中所起的作用吧,首先,我们在父盒子中设置了line-height和text-align,按理来说现在子元素就应该是垂直水平居中的了,所以只要我们让子元素变成行内块即可。为啥还要在父元素上加个font-size: 0;呢?因为字体下沉,不信你去掉font-size: 0;,你会发现位置变化了。

  然后,子元素的那些其他的属性,都是为了重置父元素继承所带来的影响。完事了~,这种方式,影响大,代码多,理解度倒是还好。总之不建议在布局中使用。

四、writing-mode + text-align + vertical-align

  说实话我不太想写这个东西,但是由于第一部分写了writing-mode,而且它确实有一定的使用场景,当然不是在这。所以还是写一下吧。

  再说句实话,我写到这里已经写的有点烦了~~为啥……,因为不能多说,多说就是每个属性的规范,又不能不说,就是咽咽不下去,吐吐不出来的感觉,好难受~

  好吧,我们看代码,这回的代码,有点恶心,不建议在生产中使用这套方案来实现垂直水平居中!

 

#grandpa {
  writing-mode: vertical-lr;
  text-align: center;
  box-sizing: border-box;
  height: 200px;
  width: 200px;
  border: 1px solid;
  background: pink;
}
#father {
  writing-mode: horizontal-tb;
  display: inline-block;
  text-align: center;
  width: 100%;
  border: 1px solid;
  background: blue;
}
#child {
  border: 1px solid;
  background: red;
  display: inline-block;
  margin: auto;
  text-align: left;
}

 

  DOM是这样的:

<div id="grandpa">
  <div id="father">
    <div id="child">我是文字我</div>
  </div>
</div>

  效果是这样的:

CSS之垂直水平居中的背后

 

 

   嗯。。。。这个为啥需要三层?因为它先改变了爷爷那层的文字流向,就影响了爸爸,然后爸爸在让儿子居中,就实现了。但是,这种东西,已经不适合生产实践了。只能作为学习理解。

  好啦,本篇到此终于结束了,其实还有些方案我没写,比如absolute+负margin,比如absolute+calc,我觉得现在这些差不多可以了,足够表达出我想表达的内容了。

  这篇文章想要提供给你的并不是鱼,而是渔,嗯……希望我做到了。

  最后~如果你能看到这里,感谢屏幕前的你能把这么混乱的一篇文章看完,嘻嘻~

参考资料: