受豎向滾動條滑動影響,當滾動條滑動到設置的位置時;再次往下滾動期望html中某個元素可以像粘在屏幕上一樣,位置不隨滾動條的滑動而變動。
效果如下:
實現過程有一些特別要注意的事項,我沒有直接貼最終代碼,而是一步步來做,記錄錯誤過程和現象:
首先准備好多個元素,這里准備了大概36個div,里面內容都是“這是一行文字”,需要粘性布局的div內容就是“粘性布局的文字”,然后一些基本的css
<style> *{margin:0;padding:0;}div{margin:20px;height: 20px;line-height: 30px;} </style> <div>這是一行文字</div> <div>這是一行文字</div> <div>這是一行文字</div> <div>這是一行文字</div> <div>這是一行文字</div> <div>這是一行文字</div> <div>這是一行文字</div> <div>這是一行文字</div> <div>這是一行文字</div> <p id="target" style="top: 40px;background-color: blueviolet;color: #fff;width: 120px;">粘性布局的文字</p> </div> <div>這是一行文字</div> <div>這是一行文字</div> <div>這是一行文字</div> ··· ··· ···
其次,假設粘性布局的元素定位的top為40px,即元素變為粘性元素的時候距離可視區域的頂部距離為40px;
然后添加js代碼,監聽滾動條滾動事件;當粘性元素距離可視區域頂部距離為40px時,設置粘性元素position:fixed;否則position置為空;
window.onload = function () { let target = document.querySelector('#target') window.addEventListener('scroll', handleScroll, false) function handleScroll() { let top = target.getBoundingClientRect().top //獲取元素可視區域頂部的距離 console.log(top); if (top < 40 || top == 40) { sticky() } else { reset() } } function sticky() { target.style.position = 'fixed' } function reset() { target.style.position = '' } }
此時效果是有點問題的,如下,控制台打印的數字是粘性元素距離可視區域頂部的距離,即 target.getBoundingClientRect().top:
問題一,會看到當鼠標往下滑動時會一閃一閃的,控制台打印的數字往下滑動會一直是60和一個負數;負數是正常的 target.getBoundingClientRect().top,而60則是position是fixed之后的top值,這是60而不是40是因為粘性元素設置了margin:20px;
而一閃一閃的原因是:
1、當滾動條往下滑動,粘性元素的top小於40時,會執行sticky方法,然后position為fixed,top是60
2、再往下滑動,此時top是60,那么又會去執行reset方法;此時position為空,粘性元素回到了最初的狀態,此時的top就是負數了
3、然后再往下滑動,檢測到top小於40,那么就會執行sticky方法;然后繼續滑動就會重復上述步驟了;
問題二,當然這里把margin:20去掉就不會出現一閃一閃了,但是會出現新的問題,(這里去掉margin並不是最終解決一閃一閃的方法,因為后面分析中設置了margin:20px也是不會出現一閃一閃的情況;)
1、當滑動到小於40后,此時粘性元素的position為fixed,top設置成了40px
2、滾動條繼續向下滑動,或者元素的top值,target.getBoundingClientRect().top,因為之前top被設置成了40px,所以這里獲取的top就會一直是40px
解決辦法:
粘性元素布局的時候需要用一個占位元素將粘性元素包裹起來
·· ·· <div>這是一行文字</div> <div>這是一行文字</div> <div id="targetBox"> <div id="target" style="top: 40px;background-color: blueviolet;color: #fff;width: 120px;">粘性布局的文字</div> </div> <div>這是一行文字</div> <div>這是一行文字</div> ·· ··
當target的position fixed和空互相切換時,占位元素依然在那里,而且這里判斷粘性元素的top值時,需要判斷的是targetBox的top值,而不是target的top,這也就解決了問題一的一閃一閃和問題二;
window.onload = function () { let active = false //是否已經是粘性布局了 let target = document.querySelector('#target') let targetBox = document.querySelector('#targetBox') window.addEventListener('scroll', handleScroll, false) function handleScroll() { let top = targetBox.getBoundingClientRect().top //獲取元素可視區域頂部的距離 console.log(top); if (top < 40 || top == 40) { sticky() } else { reset() } } function sticky() { if (active) { return } //已經是粘性布局了,則不再往下執行 target.style.position = 'fixed' active = true } function reset() { if (!active) { return } target.style.position = '' active = false } }