您好,登錄后才能下訂單哦!
前言
我入職第二家公司接到的第一個需求就是修復之前外包做的滾動吸頂效果。我當時很納悶為何一個滾動吸頂會有 bug,后來我查看代碼才發現直接用的是 offsetTop 這個屬性,而且并沒有做兼容性處理。
offsetTop
用于獲得當前元素到定位父級( element.offsetParent )頂部的距離(偏移值)。
定位父級 offsetParent 的定義是:與當前元素最近的 position != static 的父級元素。
或許寫這個代碼的人沒有注意到“定位父級”這個這個附屬條件。
后來在項目中總會遇到滾動吸頂的效果需要實現,現在我將我知道的 4 種滾動吸頂實現方式做詳細介紹。
目錄
望給個 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 元素的左內邊框之間的像素距離;
注意事項
遇到的兩個問題
一、吸頂的那一刻伴隨抖動
出現抖動的原因是因為:在吸頂元素 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種滾動吸頂實現方式的比較詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對億速云網站的支持!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。