Sticky 粘黏布局
position: sticky
如果不考虑兼容性情况下,强烈建议使用css实现,少写好多代码
css 新特性 —— sticky
对于设置了 position: sticky 的元素来说,本文我暂且描述成“粘性定位元素”,要满足以下条件才会产生所谓的“粘性”:
当你到达我的滚动位置时我会粘在屏幕上
- 一定要设置方位属性(top/left/bottom/right)
- 粘性定位元素(不含 margin)与其最近的一个祖先 scrolling box(含 border,padding)的距离,小于等于设定的方位属性阈值。如果没有拥有 scrolling box 的话会根据 viewport 来计算
- 怎么判断是否达到阈值,是根据这个做判断的 scrolling box 的滚动事件确定的,言外之意即,该 scrolling box 一定可滚动(在你设定的方向属性的方向上,如你要垂直滚动时固定,就垂直方向一定要可滚动),只有滚动了监听到了才会生效(`意味着如果设置了 overflow: hidden 是不起效的`),而且,不会受到其他祖先 scrolling box 的滚动影响。
- 父元素可视区域能容纳下粘性定位元素。一般发生在父元素不是滚动容器时。这点具体下面会说明一个情况。
- 主要是让 body 成为滚动容器,即滚动条所属的元素。
- left-section 和 right-section 各占页面的左右两边
- 导航栏.nav-bar 被一个父元素 nav-bar-wrap 包裹,目的是占位!当导航栏.nav-bar 吸顶后,设置了 position: fixed,脱离了文档流,如果没有这个父元素占位,页面的内容就会往上填补这个空缺,且,吸顶效果的瞬间页面很不流畅!
- .right-section 设置了 position 了,成为了导航内容和导航栏的 offsetParent 了,并不是滚动容器 body 了,这个是要注意的,且 body 设置了 padding-top: 24px 了。
我们汇总以及简化下上面条件的描述:
粘性定位元素要位于一个可滚动容器里,且一定要设置方向属性,该值用作与最近的一个祖先 scrolling box 的距离做比较,小于等于时生效。但是其父元素在滚动的影响下,如果可视区域容纳不下该粘性定位元素时,则粘性同样会消失。
传统实现
没滚动到位置时也会展示
二者结合
二者结合的意思是,根据浏览器是否支持sticky,来判断使用css方式的吸顶效果,还是js控制吸顶。
不过说句实话,除非你明确知道你开发的页面是应用在哪个浏览器上(或者有这个需求),这样的话你在开发时就只写合适的那段代码就好了。
但是如果你自己也不确定是应用在什么浏览器上,或者说本来是要适应大部分浏览器的话,二者结合的方案并不会省去写代码的功夫,就是说两个实现方式都要写,还要写判断,实际用哪个方式。这样做意义仅仅是,能使用css的就用css尽量减少dom操作,是性能上的优化。如果你没有这个追求的话,其实完全可以写传统的。
传统实现
- 监听scroll事件,当滚动距离达到要吸顶的条件时(基于导航栏的offsetTop进行计算判断),导航栏设置为position: fixed;
- 记录每个导航对应的内容的offsetTop,一般当滚动距离大于等于对应内容的offsetTop时,设置导航栏的选中状态;
- 点击导航栏的导航,设置滚动容器的scrollTop,一般是设置成内容的offsetTop;
上面是一个最简单的思路。当然中间会有很多细节需要注意的,我们在下面一步步实现中去了解这些细节:
具体实现细节:
- 当引起滚动的容器,不是导航对应的内容的offsetParent(后面解释offsetParent),所以在判断scrollTop与内容的offsetTop时,要加额外的一些计算。
- 当导航栏并不是像顶部导航那种在页面dom结构中比较顶层的,且宽是屏幕宽度长的这种典型的情况时,如在页面dom树中比较里层的某个div下的小导航,要进行吸顶时,由于设置了position:fixed;,宽高值如果是相对值,如百分比时,相对的基准就发生了变化了,就要处理好它的宽高情况了。
- 当页面上的内容发生了变化,如加载数据,页面发生重排重绘,此时就要更新导航栏自身以及每个导航对应内容所在容器的offsetTop,不然会影响后续的计算判断。这点是比较重要的,毕竟现在很多页面已经不是静态的了,也不是整个页面进行刷新的,都是局部刷新的了。
- 当浏览器屏幕发生变化时(resize),由于可能引起重排重绘,所以还是要更新导航栏自身以及每个导航对应内容所在容器的offsetTop,不然会影响后续的计算判断。
- 当滚动条到达底部,如果还不到导航栏最后一个导航被选择的条件时,就要强制选中最后一个导航。这点是交互上的小优化。