分类
css

使用 `position: sticky` 的要点

position 属性非常重要,它有五个可选值。“这五个选项是哪些?它们的作用如何?”是我非常喜欢的面试题。以我的经验,凡是这道题答得好的,后面多半没啥问题;这道题答不出或者错漏的,后面能翻盘的概率很低。

属性及释义大家请看 MDN,本文不再赘述。

position: sticky 比较复杂,简单来说,它包含以下特性:

  1. 当它的位置让它可以正常呈现的时候,它的定位等同于 position: static,随着正常的文档流滚动
  2. 当它的位置不足以让它正常显示,但它的父元素有足够多让它显示的空间,它的定位等同于 position: fixed
  3. 当它的父元素的空间不够让它显示,它的定位等同于 position:absolute

言说太复杂,大家可以看下这个演示,基本上就能明白了:

最近我想用这个样式完成一个侧边栏导航的需求,如下图所示:

很简单,这是一个文档页面,屏幕分成两列,左侧是侧边栏导航,右侧是文档内容。文档内容很长,用户需要滚动阅读。滚动过程中,左侧的导航栏固定不动,方便用户跳走或选择章节。因为有一个顶部通栏,所以我需要导航滚动大约70px后再固定,那么这里很合适用 position:sticky

然而事与愿违,我给侧边栏导航加上 position:sticky 之后,它的表现和开始并无二致,看起来似乎是属性没有生效。我把 CSS 放在这里,大家可以看看能不能猜到问题在哪里:

.doc-list
  display flex

  #sidebar
    position sticky
    top 0

.doc-container
  flex 1
  padding-top 1rem

  .container
    max-width 720px
    margin 0 auto

这个问题困扰了我很长时间,直到我看到这篇文章:CSS Position Sticky – How It Really Works! 它并没有直接解答我的疑问,不过它给了我一些启示:刚才说的行为特征,翻译过来,其实隐含着一条规则:

position:sticky 对象的父元素的尺寸必须比它大很多。

我用开发者工具一看,果然,因为我只设置了 display:flex,没有定义 align-items,所以它取默认值 stretch 也就是拉伸,所以我的侧边栏和右边文档容器的高度一样,所以滚动的时候,整个侧边栏都跟着一起滚,自然不会有任何效果。

于是我在第三行增加了 align-items: flex-start,立刻就获得了令人满意的效果。

.doc-list
  display flex
+ align-items flex-start

由meathill

爱编程,爱旅游,爱吐槽。
今年的目标是完成并运营至少一个 Side Project。
《Electron + Vue 实战开发》龟速创作中……

欢迎吐槽,请勿装死

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据