4 种滚动吸顶实现方式的比较

前言

我入职第二家公司接到的第一个需求就是修复之前外包做的滚动吸顶效果。我当时很纳闷为何一个滚动吸顶会有 bug,后来我查看代码才发现直接用的是 offsetTop 这个属性,而且并没有做兼容性处理。

offsetTop

用于获得当前元素到定位父级( element.offsetParent )顶部的距离(偏移值)。

定位父级 offsetParent 的定义是:与当前元素最近的 position != static 的父级元素。

或许写这个代码的人没有注意到“定位父级”这个这个附属条件。
后来在项目中总会遇到滚动吸顶的效果需要实现,现在我将我知道的 4 种滚动吸顶实现方式做详细介绍。

目录

  1. 使用 position:sticky 实现
  2. 使用 JQuery 的 offset().top 实现
  3. 使用原生的 offsetTop 实现
  4. 使用 obj.getBoundingClientRect().top 实现

望给个 star 支持一下。

四种实现方式

我们先看一下效果图:

一、使用 position:sticky 实现

1、粘性定位是什么?

粘性定位 sticky 相当于相对定位 relative 和固定定位 fixed 的结合;在页面元素滚动过程中,某个元素距离其父元素的距离达到 sticky 粘性定位的要求时;元素的相对定位 relative 效果变成固定定位 fixed 的效果。

MDN 传送门

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,代码如下:

// html
<div class="pride_tab_fixed" ref="pride_tab_fixed">
  <div class="pride_tab" :class="titleFixed == true ? 'isFixed' :''">
    // some code
  </div>
</div>

// vue
export default {
  data(){
   return{
    titleFixed: false
   }
  },
  activated(){
   this.titleFixed = false;
   window.addEventListener('scroll', this.handleScroll);
  },
  methods: {
   //滚动监听,头部固定
   handleScroll: function () {
    let offsetTop = this.$refs.pride_tab_fixed.getBoundingClientRect().top;
    this.titleFixed = offsetTop < 0;
    // some code
   }
  }
 }

offsetTop 和 getBoundingClientRect() 区别

1. getBoundingClientRect():

用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置。不包含文档卷起来的部分。
该函数返回一个 object 对象,有6个属性:
top, right, buttom, left, width, height。
(在 IE 中,默认坐标从(2,2)开始计算,只返回 top,lef,right,bottom 四个值)

2. offsetTop:

用于获得当前元素到定位父级( element.offsetParent )顶部的距离(偏移值)。

定位父级 offsetParent 的定义是:与当前元素最近的 position != static 的父级元素。

offsetTop 和 offsetParent 方法相结合可以获得该元素到 body 上边距的距离。代码如下:

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

延伸知识点

offsetWidth:

元素在水平方向上占用的空间大小:
offsetWidth =  border-left + padding-left + width + padding-right + border-right

offsetHeight:

元素在垂直方向上占用的空间大小:
offsetHeight =  border-top + padding-top + height + padding-bottom + border-bottom

注:如果存在垂直滚动条,offsetWidth 也包括垂直滚动条的宽度;如果存在水平滚动条,offsetHeight 也包括水平滚动条的高度;

offsetTop:

元素的上外边框至 offsetParent 元素的上内边框之间的像素距离;

offsetLeft:

元素的左外边框至 offsetParent 元素的左内边框之间的像素距离;

注意事项

  1. 所有偏移量属性都是只读的;
  2. 如果给元素设置了 display:none,则它的偏移量属性都为 0;
  3. 每次访问偏移量属性都需要重新计算(保存变量);
  4. 在使用的时候可能出现 DOM 没有初始化,就读取了该属性,这个时候会返回 0;对于这个问题我们需要等到 DOM 元素初始化完成后再执行。

遇到的两个问题

一、吸顶的那一刻伴随抖动

出现抖动的原因是因为:在吸顶元素 position 变为 fixed 的时候,该元素就脱离了文档流,下一个元素就进行了补位。就是这个补位操作造成了抖动。

解决方案
为这个吸顶元素添加一个等高的父元素,我们监听这个父元素的 getBoundingClientRect().top 值来实现吸顶效果,即:

<div class="title_box" ref="pride_tab_fixed">
  <div class="title" :class="titleFixed == true ? 'isFixed' :''">
  使用 `obj.getBoundingClientRect().top` 实现
  </div>
</div>

这个方案就可以解决抖动的 Bug 了。

二、吸顶效果不能及时响应

这个问题是我比较头痛,之前我没有在意过这个问题。直到有一天我用美团点外卖的时候,我才开始注意这个问题。
描述:

当页面往下滚动时,吸顶元素需要等页面滚动停止之后才会出现吸顶效果
当页面往上滚动时,滚动到吸顶元素恢复文档流位置时吸顶元素不恢复原样,而等页面停止滚动之后才会恢复原样

原因:

在 ios 系统上不能实时监听 scroll 滚动监听事件,在滚动停止时才触发其相关的事件。
解决方案:
还记得第一种方案中的 position:sticky 吗?这个属性在 IOS6 以上的系统中有良好的兼容性,所以我们可以区分 IOS 和 Android 设备做两种处理。

IOS 使用 position:sticky,Android 使用滚动监听 getBoundingClientRect().top 的值。

如果 IOS 版本过低呢?这里提供一种思路:window.requestAnimationFrame()。

以上所述是小编给大家介绍的4种滚动吸顶实现方式的比较详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • 原生js实现吸顶效果

    实现思路如下: 1. div初始居普通文档流中 2. 给window添加scroll事件(可事件节流),获取div的offset的top值,滚动时scrollTop值和top比较,当到达top时给div添加一个fixed的class使其固定 3. 向上滚动时当到达div初始top时则删除fixed的class,此时div又回到普通文档流中 4. fixed样式非IE6浏览器使用position:fixed,IE6使用position:absolute和IE expression 效果图: 代码如

  • 浅谈react.js中实现tab吸顶效果的问题

    在react项目开发中有一个需求是,页面滚动到tab所在位置时,tab要固定在顶部. 实现的思路其实很简单,就是判断当滚动距离scrollTop大于tab距离页面顶部距离offsetTop时,将tab的position变为fixed. 在react中,我在state中设置一个navTop属性,切换这个属性的值为true或者false,然后tab标签使用classnames()这个方法来利用navTop的值添加类名fixed. 一开始我是这样写的: import cs from 'classnam

  • js实现导航吸顶效果

    话不多说,请看代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <title&

  • vue实现某元素吸顶或固定位置显示(监听滚动事件)

    最近写了一个VUE的web app项目,需要实现某个部位吸顶的效果.即,页面往上滑动,刚好到达该部位时,该部分,固定在顶部显示. 1.监听滚动事件 利用VUE写一个在控制台打印当前的scrollTop, 首先,在mounted钩子中给window添加一个滚动滚动监听事件, mounted () { window.addEventListener('scroll', this.handleScroll) }, 然后在方法中,添加这个handleScroll方法 handleScroll () {

  • 4 种滚动吸顶实现方式的比较

    前言 我入职第二家公司接到的第一个需求就是修复之前外包做的滚动吸顶效果.我当时很纳闷为何一个滚动吸顶会有 bug,后来我查看代码才发现直接用的是 offsetTop 这个属性,而且并没有做兼容性处理. offsetTop 用于获得当前元素到定位父级( element.offsetParent )顶部的距离(偏移值). 定位父级 offsetParent 的定义是:与当前元素最近的 position != static 的父级元素. 或许写这个代码的人没有注意到"定位父级"这个这个附属条

  • vue实现吸顶、锚点和滚动高亮按钮效果

    因公司后台管理系统很多功能技术老旧,最近在用vue重构公司的后台管理系统,在做商品管理添加商品这一块,借鉴淘宝的添加商品的交互,需要实现一个简单的吸顶.锚点和滚动高亮按钮的效果. 需求 滚动页面到顶部,实现某元素固定到顶部效果 点击某个按钮,页面滚动到相应的位置 滚动页面,当到达某个位置时,高亮对应的相关按钮 元素吸顶实现方式 关于元素吸顶效果,通过查阅相关资料和相关测试,有三种方式(还有一种是jquery的方法,这里就不介绍了) 一.使用position:sticky 1. 什么是positi

  • 移动端吸顶fixbar的解决方案详解

    需求背景 经常会有这样的需求,当页面滚动到某一个位置时,需要某个页面元素固定在屏幕顶部,并且有时需要连续滚动吸顶.在PC端主要的实现是通过 CSS 的 position: fixed 属性,但是在移动端,尤其是在安卓端,存在诸多的兼容性问题. 问题 position:fixed给移动端带来的问题: IOS8在页面滚动时,吸顶不连续:页面滑动时,不见吸顶,页面滚动停止后,吸顶缓慢出现 滚动到顶部之后,会出现两个一样的吸顶, 过一会才恢复正常. footer底部输入框 focus 状态,footer

  • vue中多路由表头吸顶实现的几种布局方式

    vue项目多路由表头吸顶实现的几种布局方式 因为项目较大,有五个界面,每个界面有四个子组件,每个子组件都有一个table表格,需求是每个界面的每个table滚动到顶端表头吸顶,所以尝试用vux做这种需求, 一.先聊js. A.首先在vux可以这样设置. 1.在state文件中设置状态. techo:{ partsFixed:false, repairFixed:false, mateFixed:false, outRepairFixed:false }//吸顶状态 2.在action中commi

  • 微信小程序实现列表滚动头部吸顶的示例代码

    本文介绍了小程序头部吸顶的实现代码示例,分享给大家,也给自己留个笔记 demo 地址: https://github.com/iotjin/Jh_weapp 效果图: 吸顶主要是 position: sticky; .header { background: rgb(230, 230, 230); height: 25px; line-height: 25px; padding-left: 30rpx; font-size: 13px; align-items: center; position

  • vue 自定指令生成uuid滚动监听达到tab表格吸顶效果的代码

    utils/index,.js /** * 生成UUID * @param withSeparator 是否有分割符 * @returns {string} */ export function generateUUID(withSeparator = true) { let d = new Date().getTime() if (window.performance && typeof window.performance.now === 'function') { d += perf

  • react-native滑动吸顶效果的实现过程

    前言 最近公司开发方向偏向移动端,于是就被调去做RN(react-native),体验还不错,当前有个需求是首页中间吸顶的效果,虽然已经很久没写样式了,不过这种常见样式应该是so-easy,没成想翻车了,网上搜索换了几个方案都不行,最后去github上复制封装好的库来实现,现在把翻车过程记录下来. 需求效果 翻车过程 第一种方案 失败 一开始的思路是这样的,大众思路,我们需要监听页面的滚动状态,当页面滚动到要吸顶元素所处的位置的时候,我们设置它为固定定位,不过很遗憾,RN对于position属性

  • 实现一个 Vue 吸顶锚点组件方法

    前言 近期产品小哥哥给我提了一个新需求,在一个页面的滚动区中添加一组锚点定位按钮,点击按钮将对应的元素显示在页面的可视区中.当按钮组超出页面可视区的时候将其固定在滚动区域的头部,当滚动区滚动时,高亮距离滚动区顶部最近的元素所匹配的锚点按钮. 拆分功能点 现在我们已经明确需求了,接下来我们总结一下这个需求有哪些功能点: 按钮组要有吸顶效果 点击按钮要有锚点定位功能 滚动内容区需要找到对应的按钮并高亮 吸顶组件 要做一个吸顶效果最简单的方式是将 css 的 position 属性设置为 sticky

  • 小程序自定义模板实现吸顶功能

    本文实例为大家分享了小程序实现吸顶功能的具体代码,供大家参考,具体内容如下 //如图所示----这里用了自定义组件实现了小程序吸顶功能 //WXML <view class="i-sticky-demo"> <i-sticky scrollTop="{{scrollTop}}"> <i-sticky-item i-class="i-sticky-demo-title"> <view slot="

随机推荐