绝对定位咋居中?试试这新三件套,place-self搭配inset直接搞定

1,808字
8–11 分钟
in

有没有遇到过这种情况,想把一个绝对定位的元素摆到正中间,结果翻来覆去还是那套老代码,top、left各50%,再translate拽回来一半。虽然这招管用了几十年,但每次写总感觉少了点丝滑感。最近发现CSS里藏了个新玩法,不用算来算去,三行代码就能让绝对定位的元素稳稳站中间,而且全浏览器都买账。

目录

啥是place-self

place-self其实就是align-selfjustify-self这俩兄弟的合体版,在一个声明里同时搞定垂直和水平方向的对齐。原本这哥几个是给网格布局或者弹性盒子里的子元素准备的,用来指定它们在各自格子里的站位。现在这俩属性也能用在绝对定位元素上了,但有个小机关,不能直接拿来就用。

老办法咋写

想当年为了让绝对定位元素居中,代码都是这么写的:

.old-school {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

先把元素的左上角怼到父容器正中间,再用位移把自己往回拉一半的宽高,这样整个元素才真正居中。这法子没毛病,就是每次都要手动算偏移量,要是元素尺寸变了还得跟着改,不够智能。

新招式咋玩

新版方案更贴近直觉,直接把想居中的想法写进代码里:

.modern-way {
  position: absolute;
  place-self: center;
  inset: 0;
}

place-self: center告诉浏览器“我要在可用空间里居中”,而inset: 0则是把toprightbottomleft全部归零。这里有个关键点,inset属性不光是在固定元素的边距,它实际上是在重新定义所谓的“inset-modified containing block”(简称IMCB)。默认情况下绝对定位元素的IMCB和它自己的尺寸一样大,这时候让它在里面居中,等于是自己跟自己玩,根本动不了。一旦通过inset把四条边都拉到容器的边缘,IMCB就被撑开了,这时候place-self: center才有了真正的参照物,元素就能在父容器里准确居中。

容器有内边距咋整

如果父容器本身有padding,绝对定位元素居中的位置可能会显得不太对。想留出空隙有两种路子,一种是给绝对定位元素本身加margin,比如margin: 20px,它会在居中位置的基础上再往外扩一圈。另一种更直接,改inset的值,写成inset: 20px,相当于把IMCB的边界往里收了一圈,元素自然就跟容器边缘拉开了距离。

.with-spacing {
  position: absolute;
  place-self: center;
  inset: 20px;  /* 四边都留20像素空隙 */
}

角落定位也行

既然能居中,那想贴边自然也不在话下。place-self支持startend这些关键词,配合inset的调整,可以快速实现各种角落定位。

.stick-to-top-left {
  position: absolute;
  place-self: start start;  /* 第一个start是垂直,第二个是水平 */
  inset: 0;
}

.stick-to-bottom-right {
  position: absolute;
  place-self: end end;
  inset: 0;
}

要是只想贴某一边,inset也能单独指定。比如想让元素靠右但上下居中,可以这样:

.align-right-middle {
  position: absolute;
  place-self: center end;
  inset: 0;
}

另一种实现思路

虽然place-selfinset的三行代码足够简洁,但偶尔碰到老项目或者需要更精细控制的情况,还有一种备选方案,就是用margin: auto配合inset来实现居中,这招也支持所有浏览器。

.alternative-way {
  position: absolute;
  inset: 0;
  margin: auto;
}

这里margin: auto会根据inset定义出的空间自动分配外边距,让元素在四个方向上受力均衡,自然就居中了。而且这种方法在需要给元素指定固定宽高的时候特别好用,比如想让一个200×200的方块在父容器里居中,这俩搭配起来非常稳。

.fixed-size-box {
  position: absolute;
  inset: 0;
  width: 200px;
  height: 200px;
  margin: auto;
}