溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

4 種滾動(dòng)吸頂實(shí)現(xiàn)方式的比較

發(fā)布時(shí)間:2020-08-29 21:10:28 來源:腳本之家 閱讀:174 作者:小生方勤 欄目:web開發(fā)

前言

我入職第二家公司接到的第一個(gè)需求就是修復(fù)之前外包做的滾動(dòng)吸頂效果。我當(dāng)時(shí)很納悶為何一個(gè)滾動(dòng)吸頂會(huì)有 bug,后來我查看代碼才發(fā)現(xiàn)直接用的是 offsetTop 這個(gè)屬性,而且并沒有做兼容性處理。

offsetTop

用于獲得當(dāng)前元素到定位父級( element.offsetParent )頂部的距離(偏移值)。

定位父級 offsetParent 的定義是:與當(dāng)前元素最近的 position != static 的父級元素。

或許寫這個(gè)代碼的人沒有注意到“定位父級”這個(gè)這個(gè)附屬條件。
后來在項(xiàng)目中總會(huì)遇到滾動(dòng)吸頂?shù)男Ч枰獙?shí)現(xiàn),現(xiàn)在我將我知道的 4 種滾動(dòng)吸頂實(shí)現(xiàn)方式做詳細(xì)介紹。

目錄

  1. 使用 position:sticky 實(shí)現(xiàn)
  2. 使用 JQuery 的 offset().top 實(shí)現(xiàn)
  3. 使用原生的 offsetTop 實(shí)現(xiàn)
  4. 使用 obj.getBoundingClientRect().top 實(shí)現(xiàn)

望給個(gè) star 支持一下。

四種實(shí)現(xiàn)方式

我們先看一下效果圖:

4 種滾動(dòng)吸頂實(shí)現(xiàn)方式的比較

一、使用 position:sticky 實(shí)現(xiàn)

1、粘性定位是什么?

粘性定位 sticky 相當(dāng)于相對定位 relative 和固定定位 fixed 的結(jié)合;在頁面元素滾動(dòng)過程中,某個(gè)元素距離其父元素的距離達(dá)到 sticky 粘性定位的要求時(shí);元素的相對定位 relative 效果變成固定定位 fixed 的效果。

MDN 傳送門

2、如何使用?

使用條件:

父元素不能 overflow:hidden 或者 overflow:auto 屬性
必須指定 top、bottom、left、right 4 個(gè)值之一,否則只會(huì)處于相對定位
父元素的高度不能低于 sticky 元素的高度
sticky 元素僅在其父元素內(nèi)生效

在需要滾動(dòng)吸頂?shù)脑丶由弦韵聵邮奖憧梢詫?shí)現(xiàn)這個(gè)效果:

.sticky {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
}

 3、這個(gè)屬性好用嗎?

我們先看下在 Can I use 中看看這個(gè)屬性的兼容性:

4 種滾動(dòng)吸頂實(shí)現(xiàn)方式的比較

可以看出這個(gè)屬性的兼容性并不是很好,因?yàn)檫@個(gè) API 還只是實(shí)驗(yàn)性的屬性。不過這個(gè) API 在 IOS 系統(tǒng)的兼容性還是比較好的。
所以我們在生產(chǎn)環(huán)境如果使用這個(gè) API 的時(shí)候一般會(huì)和下面的幾種方式結(jié)合使用。

二、使用 JQuery 的 offset().top 實(shí)現(xiàn)

我們知道 JQuery 中封裝了操作 DOM 和讀取 DOM 計(jì)算屬性的 API,基于 offset().top 這個(gè) API 和 scrollTop() 的結(jié)合,我們也可以實(shí)現(xiàn)滾動(dòng)吸頂效果。

...
window.addEventListener('scroll', self.handleScrollOne);
...
handleScrollOne: function() {
  let self = this;
  let scrollTop = $('html').scrollTop();
  let offsetTop = $('.title_box').offset().top;
  self.titleFixed = scrollTop > offsetTop;
}
...

這樣實(shí)現(xiàn)固然可以,不過由于 JQuery 慢慢的退出歷史的舞臺(tái),我們在代碼中盡量不使用 JQuery 的 API。我們可以基于 offset().top 的源碼自己處理原生 offsetTop。于是乎就有了第三種方式。

scrolloTop() 有兼容性問題,在微信瀏覽器、IE、某些 firefox 版本中 $('html').scrollTop() 的值會(huì)為 0,于是乎也就有了第三種方案的兼容性寫法。

三、使用原生的 offsetTop 實(shí)現(xiàn)

我們知道 offsetTop 是相對定位父級的偏移量,倘若需要滾動(dòng)吸頂?shù)脑爻霈F(xiàn)定位父級元素,那么 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 的值來實(shí)現(xiàn)滾動(dòng)吸頂?shù)男Ч麊??答案是否定的?/p>

我們一同看看第四種方案。

四、使用 obj.getBoundingClientRect().top 實(shí)現(xiàn)

定義:
這個(gè) API 可以告訴你頁面中某個(gè)元素相對瀏覽器視窗上下左右的距離。
使用:
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: {
   //滾動(dòng)監(jiān)聽,頭部固定
   handleScroll: function () {
    let offsetTop = this.$refs.pride_tab_fixed.getBoundingClientRect().top;
    this.titleFixed = offsetTop < 0;
    // some code
   }
  }
 }

offsetTop 和 getBoundingClientRect() 區(qū)別

1. getBoundingClientRect():

用于獲得頁面中某個(gè)元素的左,上,右和下分別相對瀏覽器視窗的位置。不包含文檔卷起來的部分。
該函數(shù)返回一個(gè) object 對象,有6個(gè)屬性:
top, right, buttom, left, width, height。
(在 IE 中,默認(rèn)坐標(biāo)從(2,2)開始計(jì)算,只返回 top,lef,right,bottom 四個(gè)值)

2. offsetTop:

用于獲得當(dāng)前元素到定位父級( element.offsetParent )頂部的距離(偏移值)。

定位父級 offsetParent 的定義是:與當(dāng)前元素最近的 position != static 的父級元素。

offsetTop 和 offsetParent 方法相結(jié)合可以獲得該元素到 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;
  }
}

延伸知識(shí)點(diǎn)

offsetWidth:

元素在水平方向上占用的空間大?。?br /> offsetWidth =  border-left + padding-left + width + padding-right + border-right

offsetHeight:

元素在垂直方向上占用的空間大?。?br /> offsetHeight =  border-top + padding-top + height + padding-bottom + border-bottom

注:如果存在垂直滾動(dòng)條,offsetWidth 也包括垂直滾動(dòng)條的寬度;如果存在水平滾動(dòng)條,offsetHeight 也包括水平滾動(dòng)條的高度;

offsetTop:

元素的上外邊框至 offsetParent 元素的上內(nèi)邊框之間的像素距離;

offsetLeft:

元素的左外邊框至 offsetParent 元素的左內(nèi)邊框之間的像素距離;

注意事項(xiàng)

  1. 所有偏移量屬性都是只讀的;
  2. 如果給元素設(shè)置了 display:none,則它的偏移量屬性都為 0;
  3. 每次訪問偏移量屬性都需要重新計(jì)算(保存變量);
  4. 在使用的時(shí)候可能出現(xiàn) DOM 沒有初始化,就讀取了該屬性,這個(gè)時(shí)候會(huì)返回 0;對于這個(gè)問題我們需要等到 DOM 元素初始化完成后再執(zhí)行。

遇到的兩個(gè)問題

一、吸頂?shù)哪且豢贪殡S抖動(dòng)

出現(xiàn)抖動(dòng)的原因是因?yàn)椋涸谖斣?position 變?yōu)?fixed 的時(shí)候,該元素就脫離了文檔流,下一個(gè)元素就進(jìn)行了補(bǔ)位。就是這個(gè)補(bǔ)位操作造成了抖動(dòng)。

解決方案
為這個(gè)吸頂元素添加一個(gè)等高的父元素,我們監(jiān)聽這個(gè)父元素的 getBoundingClientRect().top 值來實(shí)現(xiàn)吸頂效果,即:

<div class="title_box" ref="pride_tab_fixed">
  <div class="title" :class="titleFixed == true ? 'isFixed' :''">
  使用 `obj.getBoundingClientRect().top` 實(shí)現(xiàn)
  </div>
</div>

這個(gè)方案就可以解決抖動(dòng)的 Bug 了。

二、吸頂效果不能及時(shí)響應(yīng)

這個(gè)問題是我比較頭痛,之前我沒有在意過這個(gè)問題。直到有一天我用美團(tuán)點(diǎn)外賣的時(shí)候,我才開始注意這個(gè)問題。
描述:

當(dāng)頁面往下滾動(dòng)時(shí),吸頂元素需要等頁面滾動(dòng)停止之后才會(huì)出現(xiàn)吸頂效果
當(dāng)頁面往上滾動(dòng)時(shí),滾動(dòng)到吸頂元素恢復(fù)文檔流位置時(shí)吸頂元素不恢復(fù)原樣,而等頁面停止?jié)L動(dòng)之后才會(huì)恢復(fù)原樣

原因:

在 ios 系統(tǒng)上不能實(shí)時(shí)監(jiān)聽 scroll 滾動(dòng)監(jiān)聽事件,在滾動(dòng)停止時(shí)才觸發(fā)其相關(guān)的事件。
解決方案:
還記得第一種方案中的 position:sticky 嗎?這個(gè)屬性在 IOS6 以上的系統(tǒng)中有良好的兼容性,所以我們可以區(qū)分 IOS 和 Android 設(shè)備做兩種處理。

IOS 使用 position:sticky,Android 使用滾動(dòng)監(jiān)聽 getBoundingClientRect().top 的值。

如果 IOS 版本過低呢?這里提供一種思路:window.requestAnimationFrame()。

以上所述是小編給大家介紹的4種滾動(dòng)吸頂實(shí)現(xiàn)方式的比較詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對億速云網(wǎng)站的支持!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI