你真的了解CSS中的定位吗?

253 阅读10分钟

所谓定位,就是允许你从正常的文档流中取出元素,并让它们拥有不同的行为,例如将一个元素放在另一个元素上,或者让一个元素始终保持在浏览器窗口的同一位置。本文将详细介绍有关定位(position)的相关信息,以及如何使用它们。

文档流

定位是一个相当复杂的话题,在深入了解代码之前,先让我们了解一下布局理论,并了解它的工作原理。

首先,围绕元素的内容添加任何的内边距、外边距和边框来布置一个元素盒子——这叫做盒模型。在默认情况下,块级元素内容的宽度是由其父元素宽度的100%,并且高度与其内容一致。行内元素宽高是与其中的内容匡高一致的。我们不能对行内元素设置宽或高——因为它们只是位于块级元素内容之中。如果想要通过该方法来设置行内元素的大小,则需要利用display: block将其设置成类似块级元素。

这只是对单个元素进行了解释,但元素之间是如何交互的?正常的布局是将元素放在浏览器视口内的系统。在默认情况下,块级元素会在视口内垂直布局——每个都将显示在上一个元素下面新的一行上,并利用外边距将他们分隔开来。

而行内元素则表现得不一样,它们相互之间以及任何相邻(或被包裹)的文本会位于同一行上,当然这只是在父级元素宽度足够的情况下。如果空间不够,那么多出的内容就会自动移向下一行。

当两个相邻元素都设置了外边距,并且两个外边距接触在一起时,较大的会保留下来,较小的则会消失——这叫 外边距折叠。之后会介绍。

来看个简单例子:

<h1>Basic document flow</h1>

<p>
  I am a basic block level element. My adjacent block level elements sit on new
  lines below me.
</p>

<p>
  By default we span 100% of the width of our parent element, and we are as tall
  as our child content. Our total width and height is our content + padding +
  border width/height.
</p>

<p>
  We are separated by our margins. Because of margin collapsing, we are
  separated by the width of one of our margins, not both.
</p>

<p>
  inline elements <span>like this one</span> and <span>this one</span> sit on
  the same line as one another, and adjacent text nodes, if there is space on
  the same line. Overflowing inline elements will
  <span>wrap onto a new line if possible (like this one containing text)</span>,
  or just go on to a new line if not, much like this image will do:
  <img src="long.jpg" />
</p>
body {
  width: 500px;
  margin: 0 auto;
}

p {
  background: aqua;
  border: 3px solid blue;
  padding: 10px;
  margin: 10px;
}

span {
  background: red;
  border: 1px solid black;
}

该代码会在本文中重复利用,来显示不同定位的效果。

结果如下

Snipaste_2025-01-29_10-29-04.png

介绍定位

定位的目的是允许我们做一些事情来覆盖最基本的文档流,以产生一些有趣的效果。当我们想改变布局中某些盒子的位置时,定位就是我们的帮手。或者想要创建一个浮动在页面上的UI元素,并且让它始终停留在窗口上的某个位置,无论页面如何滚动?定位都能将这种布局变为可能。

定位有许多不同的类型,要使某个元素中的特定类型产生定位,可以使用position 属性。

静态定位

静态定位是每个元素的默认值——这意味着将该元素放进它在文档流中的正确位置。

为了演示这一点,在HTML中的第二个<p>中加入一个positionedclass类:

<p class="positioned">...</p>

并添加以下声明到CSS中:

.positioned {
  position: static;
  background: yellow;
}

保存和刷新以后就会发现,第二段除了背景颜色发生了改变,其他的根本没有变化。这不就正如我们之前所说,静态定位是默认行为!

相对定位

相对定位与静态定位相似,在正常的文档流中占据属于它的默认位置,但可以通过使用topbottomleftright属性来修改该元素的最终位置。

介绍top、bottom、left、right

topbottomleftright属性是用来精确指定定位元素移动到的位置。在CSS 中的 .positioned 规则中添加以下声明:

position:relative;
top: 30px;
left: 30px;

保存后刷新会得到:

Snipaste_2025-01-29_15-47-16.png 这就是相对定位的工作方式——就像施加一个看不见的力,把定位的盒子向所设置的相反方向移动。就像我们设置了top: 30px;left: 30px;使它向下和向右移动了30px。

绝对定位

绝对定位会带来意想不到的结果,当我们代码中的定位声明修改成如下所示:

position:absolute;

会发现结果如下:

Snipaste_2025-01-29_16-04-37.png 首先,定位元素本应在文档流中的位置不再存在——第一段和第三段已经靠在一起。这就说明绝对定位的元素不再存在于文档流中,相反,它会在独属于自己的新的一层中。这非常有用:这意味着我们可以在不干扰页面上其他元素的位置前提下,创建出独立的UI功能。例如:弹出信息框和控制菜单......。

第二,该元素的位置已经发生了改变——这是因为topbottomleftright属性以不同的方式作用于绝对定位置中。它们指定的是该元素应距离包含这个元素的每条边的距离,不再是该元素应该移动的方向。所以,这时的绝对定位元素是位于从“包含元素”(也就是浏览器窗口)的顶部30px、左侧30px的位置上。

定位上下文

哪些元素是绝对定位元素的“包含元素”,这取决于绝对定位元素的父级元素的position属性。

如果所有的父级元素都没有明确的设置position属性,那么都会默认为父级元素的position属性是static。结果就是,绝对定位元素会被包含在最初始的容器中。这个初始容器和浏览器的尺寸一样,并且<HTML>元素也被包含在这个容器里面。简单来说就是绝对定位元素会被放在<HTML>元素外,并根据浏览器窗口来定位。

绝对定位元素在HTML代码中是在<body>中的,但在最后的布局中,它是在离页面(不是<body>)左侧和顶部30px的位置上。我们可以通过改变定位上下文——绝对定位元素的相对位置元素。通过设置其中一个父元素的定位属性——也就是包含绝对定位元素的那个元素(如果要设置相对元素,那相对元素一定要包含绝对定位元素)。我们将在body中添加以下代码来演示一下:

position:relative;

结果如下:

Snipaste_2025-01-29_16-52-01.png 现在第二段是相对于<body>元素进行定位的。

z-index

在绝对定位中会发生元素重叠现象,但还有一件事没有考虑——当发生重叠时,什么会决定哪些元素出现在其他元素的上面?在实例中,我们在定位上下文中的定位元素只有一个,所以它位于其他元素的上面,因为定位的元素胜于没定位的。当我们不止一个呢?

将以下代码添加到CSS中,让第一段也变成绝对定位:

p:nth-of-type(1) {
  position: absolute;
  background: lime;
  top: 10px;
  right: 30px;
}

此时,第一段将变成绿色,离开文档流,位于初始位置的上方。它也位于原始的positioned之中,并且两个重叠。这是因为 .positioned 段落是源顺序 (HTML 标记) 中的第二个段落,并且源顺序中后定位的元素将赢得先定位的元素。

如果想要更改堆叠顺序,就要用到z-index属性。“z-index”是对 z 轴的参考。通常情况下,我们都是使用水平(x 轴)和垂直(y 轴)坐标来讨论网页,以确定像背景图像和阴影偏移之类的东西的位置。

网页也存在一个Z轴:是一条从屏幕到脸的虚线。z-index值会影响定位元素在该轴上的位置;正值往上走,负值则往下。在默认情况下,元素的z-index值都是auto,实际上就是0.

当我们向p:nth-of-type(1) 规则中添加新声明:

z-index: 1;

就可以看到:

Snipaste_2025-01-29_17-22-49.png

请注意,z-index只接受无单位的值;不能指定某个元素在z-index上的值是23px。同时,z-index仅会对同一个堆叠上下文中的定位元素生效,只会将同一个堆叠上下文中的定位元素进行比较,而不会对外部堆叠上下文中的定位元素进行比较。

固定定位

还有一种定位方式——fixed。它与绝对定位的工作方式完全相同,只有一个主要区别:绝对定位是将元素固定于最近的祖先元素(如果没有,就是包含它的初始块),而固定定位是相对于浏览器窗口本身。这意味着可以创建固有的UI项目,如持久导航菜单。

首先,删除CSS中的p:nth-of-type(1) 和.positioned。再删除body中的position: relative; 并添加固定高度。如下所示:

body {
  width: 500px;
  height: 1400px;
  margin: 0 auto;
}

现在向<h1>中添加position: fixed;,并让它位于窗口顶部的中心。向CSS中添加:

h1 {
  position: fixed;
  top: 0;
  width: 500px;
  margin: 0 auto;
  background: white;
  padding: 10px;
}

top:0是为了让它贴在屏幕顶部,然后设置标题宽度让它与内容等宽,并设置margin:0 auto使它居中。再设置白色背景和内边距,让内容不会在他下面。

如果在这时保存并刷新,就会发现一个有趣的现象——标题固定,向上滚动时内容消失在其下。但我们可以进行一些改动——目前标题会遮挡一部分内容。这是因为固定定位后的标题会脱离文档流,剩下的内容会向上移。我们可以通过设置一些外边距来让它向下移动一点。添加:

p:nth-of-type(1) {
  margin-top: 60px;
}

效果如下:

Snipaste_2025-01-30_00-55-35.png

position:sticky

position:sticky相较于前面的定位方式要更新些。它相当于相对定位和固定定位的结合体。它允许被定位的元素表现得像相对定位,直到滚到某个阈值点(例如,从视口顶部起 10 像素)为止,它就会固定在那。例如,它可用于使导航栏随页面滚动直到特定点,然后粘贴在页面顶部。

向CSS中添加:

.positioned {
  position: sticky;
  top: 30px;
  left: 30px;
}

就能获得以下效果:

Snipaste_2025-01-30_01-08-03.png

Snipaste_2025-01-30_01-08-12.png

滚动索引

position:sticky还有另一种有趣的用法,那就是创建一个滚动索引页面。在该页面中,不同的标题会停留在页面顶部。代码如下:

<h1>Sticky positioning</h1>

<dl>
  <dt>A</dt>
  <dd>Apple</dd>
  <dd>Ant</dd>
  <dd>Altimeter</dd>
  <dd>Airplane</dd>
  <dt>B</dt>
  <dd>Bird</dd>
  <dd>Buzzard</dd>
  <dd>Bee</dd>
  <dd>Banana</dd>
  <dd>Beanstalk</dd>
  <dt>C</dt>
  <dd>Calculator</dd>
  <dd>Cane</dd>
  <dd>Camera</dd>
  <dd>Camel</dd>
  <dt>D</dt>
  <dd>Duck</dd>
  <dd>Dime</dd>
  <dd>Dipstick</dd>
  <dd>Drone</dd>
  <dt>E</dt>
  <dd>Egg</dd>
  <dd>Elephant</dd>
  <dd>Egret</dd>
</dl>

在文档流中,<dt>元素将随内容滚动。当我们在<dt>元素上添加position: sticky,并将top的值设置为 0,当标题滚动到视口的顶部时,支持此属性的浏览器会将标题粘贴到那个位置。随后,每个后续标题将替换前一个标题,直到它向上滚动到该位置。

dt {
  background-color: black;
  color: white;
  padding: 10px;
  position: sticky;
  top: 0;
  left: 0;
  margin: 1em 0;
}

效果如下:

Snipaste_2025-01-30_01-12-52.png

Snipaste_2025-01-30_01-13-03.png

总结

CSS中的定位是创建复杂 CSS 布局和 UI 功能背后的基本工具之一。它总共有五种——静态定位、相对定位、绝对定位、固定定位以及sticky。不同的定位方式会带来不同UI界面效果,学会合理使用这些定位效果,为你的页面带来与众不同的效果。