我入职第二家公司接到的第一个需求就是修复之前外包做的滚动吸顶效果。我当时很纳闷为何一个滚动吸顶会有 bug,后来我查看代码才发现直接用的是 offsetTop 这个属性,而且并没有做兼容性处理。
offsetTop
用于获得当前元素到定位父级( element.offsetParent )顶部的距离(偏移值)。
定位父级 offsetParent 的定义是:与当前元素最近的 position != static 的父级元素。
或许写这个代码的人没有注意到“定位父级”这个这个附属条件。
后来在项目中总会遇到滚动吸顶的效果需要实现,现在我将我知道的 4 种滚动吸顶实现方式做详细介绍。
目录
使用 position:sticky 实现
使用 JQuery 的 offset().top 实现
使用原生的 offsetTop 实现
使用 obj.getBoundingClientRect().top 实现
望给个 star 支持一下。
四种实现方式
我们先看一下效果图:
一、使用 position:sticky 实现
1、粘性定位是什么?
粘性定位 sticky 相当于相对定位 relative 和固定定位 fixed 的结合;在页面元素滚动过程中,某个元素距离其父元素的距离达到 sticky 粘性定位的要求时;元素的相对定位 relative 效果变成固定定位 fixed 的效果。
2、如何使用?
使用条件:
父元素不能 overflow:hidden 或者 overflow:auto 属性
必须指定 top、bottom、left、right 4 个值之一,否则只会处于相对定位
父元素的高度不能低于 sticky 元素的高度
sticky 元素仅在其父元素内生效
在需要滚动吸顶的元素加上以下样式便可以实现这个效果:
.sticky { position: -webkit-sticky; position: sticky; top: 0; }
3、这个属性好用吗?
我们先看下在 Can I use 中看看这个属性的兼容性:
可以看出这个属性的兼容性并不是很好,因为这个 API 还只是实验性的属性。不过这个 API 在 IOS 系统的兼容性还是比较好的。
所以我们在生产环境如果使用这个 API 的时候一般会和下面的几种方式结合使用。
二、使用 JQuery 的 offset().top 实现
我们知道 JQuery 中封装了操作 DOM 和读取 DOM 计算属性的 API,基于 offset().top 这个 API 和 scrollTop() 的结合,我们也可以实现滚动吸顶效果。
... window.addEventListener('scroll', self.handleScrollOne); ... handleScrollOne: function() { let self = this; let scrollTop = $('html').scrollTop(); let offsetTop = $('.title_box').offset().top; self.titleFixed = scrollTop > offsetTop; } ...
这样实现固然可以,不过由于 JQuery 慢慢的退出历史的舞台,我们在代码中尽量不使用 JQuery 的 API。我们可以基于 offset().top 的源码自己处理原生 offsetTop。于是乎就有了第三种方式。
scrolloTop() 有兼容性问题,在微信浏览器、IE、某些 firefox 版本中 $('html').scrollTop() 的值会为 0,于是乎也就有了第三种方案的兼容性写法。
三、使用原生的 offsetTop 实现
我们知道 offsetTop 是相对定位父级的偏移量,倘若需要滚动吸顶的元素出现定位父级元素,那么 offsetTop 获取的就不是元素距离页面顶部的距离。
我们可以自己对 offsetTop 做以下处理:
getOffset: function(obj,direction){ let offsetL = 0; let offsetT = 0; while( obj!== window.document.body && obj !== null ){ offsetL += obj.offsetLeft; offsetT += obj.offsetTop; obj = obj.offsetParent; } if(direction === 'left'){ return offsetL; }else { return offsetT; } }
使用:
... window.addEventListener('scroll', self.handleScrollTwo); ... handleScrollTwo: function() { let self = this; let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; let offsetTop = self.getOffset(self.$refs.pride_tab_fixed); self.titleFixed = scrollTop > offsetTop; } ...
你是不是看出了以上两种方式的一些问题?
我们一定需要使用 scrollTop - offsetTop 的值来实现滚动吸顶的效果吗?答案是否定的。
我们一同看看第四种方案。
四、使用 obj.getBoundingClientRect().top 实现
定义:
这个 API 可以告诉你页面中某个元素相对浏览器视窗上下左右的距离。
使用:
tab 吸顶可以使用 obj.getBoundingClientRect().top 代替 scrollTop - offsetTop,代码如下: