CSS 中的 position
属性用于指定一个元素在文档中的定位方式,在日常开发工作中使用频率是非常高的,但有一些特性有可能会被我们遗忘,这些特性有可能造成意想不到的影响,也有可能带来意想不到的惊喜,所以,理清一些特性还是很有必要的。
这篇文章就来温习一下 CSS 中的定位(position)。
static
将 position
设为此值时,元素会根据正常文档流进行放置,设置 top
、right
、bottom
、left
和 z-index
是无效的。
这也是 position
的默认值。
relative
将 position
设为此值时,元素会先根据正常文档流进行放置,然后根据 top
、right
、bottom
和 left
的值相对于自身进行偏移。 偏移量不会影响任何其他元素的位置;因此,页面布局中为元素提供的空间与位置等同于设置为 static
。
<div class="container">
<div class="relative">这是 relative 元素</div>
<div class="relative-after"></div>
</div>
对应的 CSS 如下:
.container {
height: 200px;
width: 400px;
border: solid 1px;
}
.relative {
position: relative;
background-color: dodgerblue;
color: white;
padding: 10px;
bottom: -100px;
}
.relative-after{
height: 70px;
background-color: yellowgreen;
}
得到的效果如下:
可以看到,relative
元素的位置改变了,而其兄弟元素的位置却没有改变。
relative
会创建新的层叠上下文,靠后的元素会“覆盖”在其之前的元素上。
absolute
将 position
设为此值时,元素会被移出正常文档流,然后根据 top
、right
、bottom
和 left
的值相对于离其最近的 position
不为 static
的祖先元素进行偏移。偏移量不会影响任何其他元素的位置。
<div class="container">
<div class="absolute">这是 absolute 元素</div>
</div>
对应的 CSS 如下:
.container {
height: 10000px;
width: 400px;
border: solid 1px;
overflow-y: scroll;
position: relative;
}
.absolute {
position: absolute;
right: 40px;
top: 40px;
background-color: dodgerblue;
color: white;
padding: 10px;
margin: 20px;
}
得到的效果如下:
事实上,当其祖先元素的 transform
、persperctive
或 filter
不为 none
时,该元素则会相对于该祖先元素进行偏移。
<div class="ancestor">
<div class="container">
<div class="absolute">这是 absolute 元素</div>
</div>
</div>
在上例的 CSS 中添加:
.ancestor {
position: relative;
}
得到的效果如下:
可以看到,absolute
元素的位置由之前的视口右上角变为了 ancestor
元素的右上角。
absolute
也会创建新的层叠上下文,靠后的元素会“覆盖”在其之前的元素上。
fixed
将 position
设为此值时,元素会被移出正常文档流,然后根据 top
、right
、bottom
和 left
的值相对于屏幕视口(viewport)进行偏移。偏移量不会影响任何其他元素的位置。
<div class="container">
<div class="fixed">这是 fixed 元素</div>
</div>
对应的 CSS 如下:
.container {
height: 10000px;
width: 400px;
border: solid 1px;
overflow-y: scroll;
}
.fixed {
position: fixed;
right: 40px;
top: 40px;
background-color: dodgerblue;
color: white;
padding: 10px;
}
得到的效果如下:
事实上,fixed
并不总是相对于屏幕视口进行偏移的,如果其祖先元素的 transform
、persperctive
或 filter
不为 none
,则会相对于该祖先元素进行偏移。
修改上例中的样式:
.container {
height: 10000px;
width: 400px;
border: solid 1px;
overflow-y: scroll;
perspective: 10px;
}
得到的效果如下:
fxied
也会创建新的层叠上下文,靠后的元素会“覆盖”在其之前的元素上。
sticky
将 position
设为此值时,元素会先根据正常文档流进行放置,然后根据 top
、right
、bottom
和 left
的值相对于离其最近的滚动祖先(scrolling ancestor)和块级祖先(block-level ancestor)进行偏移(包括表格相关元素)。偏移量不会影响任何其他元素的位置。
滚动祖先指的是 overflow
的值为 hidden
、scroll
、auto
或 overlay
的元素。
假设有如下 HTML 结构:
<div class="container">
<div class="sticky-before"></div>
<div class="sticky">这是 sticky 元素</div>
<div class="sticky-after"></div>
</div>
对应的 CSS 如下:
.container {
height: 300px;
width: 400px;
border: solid 1px;
overflow-y: scroll;
}
.sticky-before {
height: 200px;
background-color: yellow;
}
.sticky {
background-color: dodgerblue;
position: sticky;
top: 0;
color: white;
padding: 10px;
}
.sticky-after {
height: 400px;
background-color: yellowgreen;
}
得到的效果如下(必须指定一个粘滞方向,否则无效):
可以看到,当 sticky
元素滚动到顶部时就固定不动了。
当最近的滚动祖先 overflow
为 hidden
时:
.container {
height: 300px;
width: 400px;
border: solid 1px;
overflow: hidden;
}
有可能会“看不到效果”:
这是因为没有滚动产生,而设置的 top
值还没有达到触发粘滞的阈值,稍加修改即可看到效果:
.sticky {
background-color: dodgerblue;
position: sticky;
top: 250px;
color: white;
padding: 10px;
}
得到的效果如下:
我们增加了 top
值,sticky
元素偏移到了 sticky-after
元素上。
sticky
也会创建新的层叠上下文,因此在同级元素中,靠后的元素“覆盖”在其之前的元素上:
<div class="container">
<div class="sticky-before"></div>
<div class="sticky">这是 sticky 元素一</div>
<div class="sticky-after"></div>
<div class="sticky">这是 sticky 元素二</div>
<div class="sticky-after"></div>
</div>
得到的效果如下:
通过 sticky
,可以非常轻松地实现通讯录效果:
<div class="container">
<dl>
<dt>A</dt>
<dd>Andrew W.K.</dd>
<dd>Apparat</dd>
<dd>Arcade Fire</dd>
<dd>At The Drive-In</dd>
<dd>Aziz Ansari</dd>
</dl>
<dl>
<dt>C</dt>
<dd>Chromeo</dd>
<dd>Common</dd>
<dd>Converge</dd>
<dd>Crystal Castles</dd>
<dd>Cursive</dd>
</dl>
<dl>
<dt>E</dt>
<dd>Explosions In The Sky</dd>
</dl>
<dl>
<dt>T</dt>
<dd>Ted Leo & The Pharmacists</dd>
<dd>T-Pain</dd>
<dd>Thrice</dd>
<dd>TV On The Radio</dd>
<dd>Two Gallants</dd>
</dl>
</div>
注意结构,如果直接将 sticky
元素嵌入产生的是覆盖效果,对应的 CSS 如下:
* {
box-sizing: border-box;
}
.container {
height: 200px;
width: 400px;
border: solid 1px;
overflow-y: scroll;
}
.container::-webkit-scrollbar-track {
background-color: transparent;
}
.container::-webkit-scrollbar-thumb {
border-radius: 10px;
background: rgba(0, 0, 0, .2);
}
.container::-webkit-scrollbar {
width: 6px;
}
dl {
margin: 0;
}
dt {
background: #e5e5e5;
position: sticky;
top: -1px;
padding: 5px 0 5px 15px;
margin: 0;
}
dd {
padding: 5px 0;
white-space: nowrap;
margin-left: 15px;
}
dd:not(:last-child) {
border-bottom: solid 1px #ccc;
}
得到的效果如下:
可以看到,每当首字母所在的 dt
元素滚动到顶部时就固定不动了,又因为 sticky
是相对于最近的块级祖先进行偏移的,所以当 dl
滚出 container
元素时会带着 dt
元素一块儿滚出,进而得到想要的效果。
结语
技术一直在推陈出新,有些东西可能已经废弃,同时,也有新的东西发布,我们需要不时温习,面对需求才能游刃有余。