CSS 中的 position 属性用于指定一个元素在文档中的定位方式,在日常开发工作中使用频率是非常高的,但有一些特性有可能会被我们遗忘,这些特性有可能造成意想不到的影响,也有可能带来意想不到的惊喜,所以,理清一些特性还是很有必要的。

这篇文章就来温习一下 CSS 中的定位(position)。

static

position 设为此值时,元素会根据正常文档流进行放置,设置 toprightbottomleftz-index 是无效的。

这也是 position 的默认值。

relative

position 设为此值时,元素会先根据正常文档流进行放置,然后根据 toprightbottomleft 的值相对于自身进行偏移。 偏移量不会影响任何其他元素的位置;因此,页面布局中为元素提供的空间与位置等同于设置为 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 元素的位置改变了,而其兄弟元素的位置却没有改变。

relative 会创建新的层叠上下文,靠后的元素会“覆盖”在其之前的元素上。

absolute

position 设为此值时,元素会被移出正常文档流,然后根据 toprightbottomleft 的值相对于离其最近的 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;
}

得到的效果如下:

absolute

事实上,当其祖先元素的 transformpersperctivefilter 不为 none时,该元素则会相对于该祖先元素进行偏移。

<div class="ancestor">
  <div class="container">
    <div class="absolute">这是 absolute 元素</div>
  </div>
</div>
 




在上例的 CSS 中添加:

.ancestor {
  position: relative;
}

得到的效果如下:

absolute_ancestor

可以看到,absolute 元素的位置由之前的视口右上角变为了 ancestor 元素的右上角。

absolute 也会创建新的层叠上下文,靠后的元素会“覆盖”在其之前的元素上。

fixed

position 设为此值时,元素会被移出正常文档流,然后根据 toprightbottomleft 的值相对于屏幕视口(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

事实上,fixed 并不总是相对于屏幕视口进行偏移的,如果其祖先元素的 transformpersperctivefilter 不为 none,则会相对于该祖先元素进行偏移。

修改上例中的样式:

.container {
  height: 10000px;
  width: 400px;
  border: solid 1px;
  overflow-y: scroll;
  perspective: 10px;
}





 

得到的效果如下:

perspective

fxied 也会创建新的层叠上下文,靠后的元素会“覆盖”在其之前的元素上。

sticky

position 设为此值时,元素会先根据正常文档流进行放置,然后根据 toprightbottomleft 的值相对于离其最近的滚动祖先(scrolling ancestor)和块级祖先(block-level ancestor)进行偏移(包括表格相关元素)。偏移量不会影响任何其他元素的位置。

滚动祖先指的是 overflow 的值为 hiddenscrollautooverlay 的元素。

假设有如下 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

可以看到,当 sticky 元素滚动到顶部时就固定不动了。

当最近的滚动祖先 overflowhidden 时:

.container {
  height: 300px;
  width: 400px;
  border: solid 1px;
  overflow: hidden;
}




 

有可能会“看不到效果”:

overflow_hidden

这是因为没有滚动产生,而设置的 top 值还没有达到触发粘滞的阈值,稍加修改即可看到效果:

.sticky {
  background-color: dodgerblue;
  position: sticky;
  top: 250px;
  color: white;
  padding: 10px;
}



 



得到的效果如下:

top_250

我们增加了 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_stack

通过 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;
}

得到的效果如下:

address_list

可以看到,每当首字母所在的 dt 元素滚动到顶部时就固定不动了,又因为 sticky 是相对于最近的块级祖先进行偏移的,所以当 dl 滚出 container 元素时会带着 dt 元素一块儿滚出,进而得到想要的效果。

结语

技术一直在推陈出新,有些东西可能已经废弃,同时,也有新的东西发布,我们需要不时温习,面对需求才能游刃有余。

最近更新:
作者: MeFelixWang