3.4 定位
CSS布局的核心是position
属性,对元素盒子应用这个属性,可以相对于它在常规文档流中的位置重新定位。position
属性有4个值:static
、relative
、absolute
、fixed
,默认值为static
。这些属性都是什么意思?别急,我会通过以下4个段落来逐个说明。
<p>First Paragraph</p>
<p>Second Paragraph</p>
<p id="specialpara">Third Paragraph (with ID)</p>
<p>Fourth Paragraph</p>
在接下来的例子中,我会让第一段、第二段和第四段保持默认的static
定位方式,但修改第三段的position
属性。
为了不影响其他段落,我特意为第三段添加了值为specialpara
的ID。
3.4.1 静态定位
我们先看一看图3-23,这是四个段落都采用默认静态定位的效果。
图3-23 静态定位下的块级元素会在默认文档流中上下堆叠
在静态定位的情况下,每个元素在处在常规文档流中。它们都是块级元素,所以就会在页面中自上而下地堆叠起来。想想第1章我们展示的那个没有应用样式的HTML布局,那就是默认的static
文档流。
要想突破static
定位提供的这种按顺序布局元素的方式,必须把盒子的position
属性改为其他三个值。
3.4.2 相对定位
下面我们就把第三段的position
属性设置为relative
。光设置这个属性还看不出来有什么不一样,因为你只设置了它的定位方式是“相对定位”。到底相对哪里定位呢?相对的是它原来在文档流中的位置(或者默认位置)。接下来,可以使用top
、right
、bottom
和left
属性来改变它的位置了。但多数情况下,只用top
和left
就可以实现我们想要的效果。以下CSS规则
p#specialpara {position:relative; top:25px; left:30px;}
可以得到图3-24所示的结果。
图3-24 相对定位下,可以利用top
和left
属性相对于元素在文档流中的常规位置重新定位
可以给
top
和left
属性设定负值,把元素向上、向左移动。
现在,第三段与它在文档流中的默认位置相比,向下移动了25像素,向右移动了30像素。相当于它把自己从原来的包含元素(body
)中挣脱出来了,而且有一部分还跑到了屏幕之外。要注意,除了这个元素自己相对于原始位置挪动了一下之外,页面没有发生任何变化。换句话说,这个元素原来占据的空间没有动,其他元素也没动。
使用相对定位的关键是什么呢?就是要考虑到元素原来的空间。如图3-24所示,可以给第四段设置一个30像素或更大的margin-top
值,让它向下移动,从而避免被重新定位的第三段挡住。
3.4.3 绝对定位
绝对定位跟静态定位和相对定位比,绝对不一样。因为绝对定位会把元素彻底从文档流中拿出来。好,下面我们就修改例子中的代码,把relative
改成absolute
。
p#specialpara {position:absolute; top:25px; left:30px;}
图3-25显示了结果。
图3-25 绝对定位下,元素从文档流中被“连根拔起”,然后再相对于其他元素(在这里,是默认的定位上下文body
)定位
在图3-25中,可以看到元素之前占据的空间被“回收了”。这说明,绝对定位的元素完全脱离了常规文档流,它现在是相对于顶级元素body
在定位。而这自然而然就引出了一个关于定位的重要概念:定位上下文。
关于定位上下文,首先我们要知道绝对定位元素默认的定位上下文是body
元素。如图3-25所示,通过top
和left
设定的偏移值,决定了元素相对于body
元素(标记层次中的祖先容器),而不是相对于它在文档流中的位置偏移多远——这一点与相对定位的元素不同。
由于绝对定位元素的定位上下文是body
,所以在页面滚动的时候,为了维护与body
元素的相对位置关系,它也会相应地移动。
在介绍怎么给绝对定位元素设定其他定位上下文(body
之外的元素)之前,我们先把4种定位方式介绍完。接下来看一看固定定位。
3.4.4 固定定位
从完全移出文档流的角度说,固定定位与绝对定位类似。
p#specialpara {position:fixed; top:30px; left:20px;}
但不同之处在于,固定定位元素的定位上下文是视口(浏览器窗口或手持设备的屏幕),因此它不会随页面滚动而移动。图3-26和图3-27展示了固定定位的效果。
图3-26 乍一看,固定定位很像绝对定位……
图3-27 ……但滚动页面才发现,固定定位元素不随着页面滚动而移动
固定定位并不常用,最常见的情况是用它创建不随页面滚动而移动的导航元素。
好了,既然你已经理解了4个定位属性,那接下来我们好好讲一讲定位上下文吧。
3.4.5 定位上下文
把元素的position
属性设定为relative
、absolute
或fixed
后,继而可以使用top
、right
、bottom
和left
属性,相对于另一个元素移动该元素的位置。这里的“另一个元素”,就是该元素的定位上下文。
在讲绝对定位的时候,我们知道绝对定位元素默认的定位上下文是body
。这是因为body
是标记中所有元素唯一的祖先元素。而实际上,绝对定位元素的任何祖先元素都可以成为它的定位上下文,只要你把相应祖先元素的position
设定为relative
即可。
比如下面的标记
<body>
<div id="outer">
<div id="inner">This is text…</div>
</div>
</body>
请注意,对HTML中的文本应该使用恰当的语义标签来标记。我们这里为了说明问题的需要,才把文本直接放在了没有语义的
div
中。
搭配下面的CSS
div#outer {width:250px; margin:50px 40px; border-top:3px solid red;}
div#inner {top:10px; left:20px; background:#ccc;}
结果如图3-28所示。
图3-28 这里是两个嵌套在一起的div
。我们给外部div
的上方加了边框,给内部div
加了背景。由于内部div
(默认)是静态定位的,因此top
和left
属性不起作用
看到代码里给内部div
设定了top
和left
属性,你是不是觉得图3-28有问题——为什么内部div
没有相对外部div
向下移动10像素,向右移动20像素呢?为什么它们俩的原点(左上角点)还一样呢?原因在于,内外部div
默认都是静态定位,它们之间不存在谁是谁的定位上下文这个问题。换句话说,在常规文档流中,由于外部div
没有内容,内部div
就会跟它共享相同的起点。只有将元素的position
属性设定为relative
、absolute
或fixed
,这个元素的top
、right
、bottom
和left
属性才会起作用。下面我们就把内部div
设定为绝对定位,来看一看有什么变化发生。
div#outer {width:250px; margin:50px 40px; border-top:3px solid red;}
div#inner {position:absolute; top:10px; left:20px; background:#ccc;}
对了,绝对定位相对于谁呀?由于没有相对定位的祖先元素供其参照,内部div
只能以默认的定位上下文body
作为参照,相对于它定位。此时,内部div
完全无视其父元素(外部div
)的存在,top
和left
属性会相对于body
元素向下、向左偏移其位置,结果如图3-29所示。
图3-29 虽然有背景的内部div
在标记中位于外部div
(看那个边框)之中,但由于不存在相对定位的其他祖先元素可以作为定位上下文,绝对定位的内部div
只能相对于body
元素进行定位
事实上,只要把元素的外边距和内边距设定好,多数情况下只用静态定位就足以实现页面布局了。很多刚开始接触CSS的初学者都会错误地设定
position
属性,最终才发现从文档流中挪出来的这些元素一点也不好控制。因此,除非真需要那么做,否则不要轻易修改元素默认的position
属性。
如果我现在把外部div
的position
属性设定为relative
:
div#outer {position:relative; width:250px; margin:50px 40px; border-top:3px solid red;}
div#inner {position:absolute; top:10px; left:20px; background:#ccc;}
这样,绝对定位的内部div
的定位上下文就变成了外部div
,结果如图3-30所示。
图3-30 外部div
改为相对定位之后,其后代中绝对定位的元素就会按照top
和left
属性的设定,相对于外部div
定位
此时内部div
的top
和left
属性参照的就是外部div
了。如果你再用left
和top
属性重新定位外部div
,内部div
也会跟着移动相同的距离,以保证它与外部div
(也就是它的定位上下文)之间的位置关系。