所谓定位,就是允许你从正常的文档流中取出元素,并让它们拥有不同的行为,例如将一个元素放在另一个元素上,或者让一个元素始终保持在浏览器窗口的同一位置。本文将详细介绍有关定位(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;
}
该代码会在本文中重复利用,来显示不同定位的效果。
结果如下
介绍定位
定位的目的是允许我们做一些事情来覆盖最基本的文档流,以产生一些有趣的效果。当我们想改变布局中某些盒子的位置时,定位就是我们的帮手。或者想要创建一个浮动在页面上的UI元素,并且让它始终停留在窗口上的某个位置,无论页面如何滚动?定位都能将这种布局变为可能。
定位有许多不同的类型,要使某个元素中的特定类型产生定位,可以使用position 属性。
静态定位
静态定位是每个元素的默认值——这意味着将该元素放进它在文档流中的正确位置。
为了演示这一点,在HTML中的第二个<p>中加入一个positioned的class类:
<p class="positioned">...</p>
并添加以下声明到CSS中:
.positioned {
position: static;
background: yellow;
}
保存和刷新以后就会发现,第二段除了背景颜色发生了改变,其他的根本没有变化。这不就正如我们之前所说,静态定位是默认行为!
相对定位
相对定位与静态定位相似,在正常的文档流中占据属于它的默认位置,但可以通过使用top、bottom、left、right属性来修改该元素的最终位置。
介绍top、bottom、left、right
top、bottom、left、right属性是用来精确指定定位元素移动到的位置。在CSS 中的 .positioned 规则中添加以下声明:
position:relative;
top: 30px;
left: 30px;
保存后刷新会得到:
这就是相对定位的工作方式——就像施加一个看不见的力,把定位的盒子向所设置的相反方向移动。就像我们设置了
top: 30px;、
left: 30px;使它向下和向右移动了30px。
绝对定位
绝对定位会带来意想不到的结果,当我们代码中的定位声明修改成如下所示:
position:absolute;
会发现结果如下:
首先,定位元素本应在文档流中的位置不再存在——第一段和第三段已经靠在一起。这就说明绝对定位的元素不再存在于文档流中,相反,它会在独属于自己的新的一层中。这非常有用:这意味着我们可以在不干扰页面上其他元素的位置前提下,创建出独立的UI功能。例如:弹出信息框和控制菜单......。
第二,该元素的位置已经发生了改变——这是因为top、bottom、left、right属性以不同的方式作用于绝对定位置中。它们指定的是该元素应距离包含这个元素的每条边的距离,不再是该元素应该移动的方向。所以,这时的绝对定位元素是位于从“包含元素”(也就是浏览器窗口)的顶部30px、左侧30px的位置上。
定位上下文
哪些元素是绝对定位元素的“包含元素”,这取决于绝对定位元素的父级元素的position属性。
如果所有的父级元素都没有明确的设置position属性,那么都会默认为父级元素的position属性是static。结果就是,绝对定位元素会被包含在最初始的容器中。这个初始容器和浏览器的尺寸一样,并且<HTML>元素也被包含在这个容器里面。简单来说就是绝对定位元素会被放在<HTML>元素外,并根据浏览器窗口来定位。
绝对定位元素在HTML代码中是在<body>中的,但在最后的布局中,它是在离页面(不是<body>)左侧和顶部30px的位置上。我们可以通过改变定位上下文——绝对定位元素的相对位置元素。通过设置其中一个父元素的定位属性——也就是包含绝对定位元素的那个元素(如果要设置相对元素,那相对元素一定要包含绝对定位元素)。我们将在body中添加以下代码来演示一下:
position:relative;
结果如下:
现在第二段是相对于
<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;
就可以看到:
请注意,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;
}
效果如下:
position:sticky
position:sticky相较于前面的定位方式要更新些。它相当于相对定位和固定定位的结合体。它允许被定位的元素表现得像相对定位,直到滚到某个阈值点(例如,从视口顶部起 10 像素)为止,它就会固定在那。例如,它可用于使导航栏随页面滚动直到特定点,然后粘贴在页面顶部。
向CSS中添加:
.positioned {
position: sticky;
top: 30px;
left: 30px;
}
就能获得以下效果:
滚动索引
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;
}
效果如下:
总结
CSS中的定位是创建复杂 CSS 布局和 UI 功能背后的基本工具之一。它总共有五种——静态定位、相对定位、绝对定位、固定定位以及sticky。不同的定位方式会带来不同UI界面效果,学会合理使用这些定位效果,为你的页面带来与众不同的效果。