頁面中的錨點各位想必都“了如指掌”,其基于a連結“同頁面跳轉”和“id屬性标簽的曆史問題”。
可能有人不知道的是:HTML中的“id”非常神奇:id屬性的元素不僅可以在js中直接使用,而不必先擷取;還可以和url相關聯。
直接用id通路是舊版本js遺留下來的特性,浏覽器會建立window執行個體的id同名屬性,這是為了相容舊的網頁。但不要依賴這個特性,在含有特殊字元或者和window執行個體的其他屬性有沖突時可能失效。
我們都知道,錨點應該這樣用:
<a href="這裡寫#号+id名">去這裡</a>
<div id="上面a中引用的id名">我是目的地</div>
如此便可。
但是這裡面有兩個小問題:
- 使用錨點後url中也會帶有href中的字元串(#号+id名)
- 因為是類似跳轉方式,是以過程非常生硬
比如有的網頁中有這樣的效果:點選側邊欄或頂部某一個div後整個頁面滑動到相應的位置,就像這樣——
以前來說用錨點肯定是不現實的,是以我們需要找到一個适合的API。
按照需求,即點選某個div後對應部分的頂部要“剛好”到視窗可視區的頂部!這裡面我們要考慮的一定是“如何算div的頂部在視窗可視區頂部”。
js提供了一個有趣的API可以完成我們的需求 ——
getBoundingClient()
,它相當于是封裝了client API并把它應用在普通元素上,通過它我們能得到此元素距離可視區頂部、左邊的距離,比如:
并且根據上一篇文章(點選閱讀文章)中所提,document下的scrollTop API可為我們提供文檔流(頁面整體區域)向上滾動了多少距離。
筆者在這裡再次提醒各位要區分js中inner系、page系、client系、offset系、outer系、screen系…事件,他們的含義各不相同。比如常說的offsetTop/Left,是距離上一級父元素的上/左邊距,通常用 if(dom.parentElement)
做累加求的是“距離文檔流頂部的距離”,就不适用于此處。
将二者結合起來使用,就可以達到:若想要展示的區域在下方,就讓文檔流不斷向上滾動,直到此區域滑動到可視區頂端;反之若想要展示的區域已經滑過,就讓文檔流不斷向下滾動。
/**
leftBtn:側邊欄中所有負責頁面滾動的div,都有一個class屬性為left-btn
i:left-btn所屬div的下标(第幾個)
.mao1/2/3:頁面中負責“滑到頂部”的div,一般是某一個區域的小标題
*/
let leftBtn=document.querySelectorAll(".left-btn")
function gotoTop(i,top){
var timer=null;
leftBtn[i].onclick=function(){
let scrollTop=document.body.scrollTop ||document.documentElement.scrollTop || window.pageYOffset
// 若目标位置在目前位置上方
if(scrollTop>top){
clearInterval(timer)
timer=null;
timer=setInterval(function(){
scrollTop=scrollTop-90<top?top:scrollTop-90;
document.body.scrollTop=document.documentElement.scrollTop=window.pageYOffset=scrollTop
if(scrollTop<=top){
clearInterval(timer);
timer=null;
}
},50)
// 目标位置在目前位置下方
}else if(scrollTop<top){
clearInterval(timer)
timer=null;
timer=setInterval(function(){
scrollTop=scrollTop+90>top?top:scrollTop+90;
document.body.scrollTop=document.documentElement.scrollTop=window.pageYOffset=scrollTop
if(scrollTop>=top){
clearInterval(timer);
timer=null;
}
},50)
}
}
}
使用如:
let one_top=document.querySelector(".mao1").getBoundingClientRect().top
let two_top=document.querySelector(".mao2").getBoundingClientRect().top
let thr_top=document.querySelector(".mao3").getBoundingClientRect().top
gotoTop(0,one_top);
gotoTop(1,two_top);
gotoTop(2,thr_top);
這樣就能達到上面展示的效果了。
這裡面需要注意的是:
setInterval()
定時器的清除!
如果上方代碼去掉第14、15、26、27行,則頁面滾動行為會發生錯誤。看上去似乎是
失效了!其實這個問題本身是由于js定時器特性産生的。
clearInterval
是根據定時器本身的辨別來進行清除的,如果在期間生成了新的interval,并覆寫timer辨別對象,舊有的timer定時器對象并不會被停止和清除,而且辨別也會丢失導緻再也無法被清除,是以寫定時器時一定要注意。建議在調用某個定時器任務的函數的時候,一定一開始就把之前的定時器先清除!
clearInterval
這個問題到這裡似乎很好的解決了。但是上面代碼畢竟還是太長了,而且裡面要進行嚴格的判斷以決定開始和結束(定時器的增加和删除),似乎稍有不妥。
不過别急,js更新了另一個神器 ——
scrollIntoView()
,原先這個API的作用就是讓指定的div的頂部跳到視窗可視區頂部(從效果上看類似錨點),但是更新後增加了配置參數:
dom.scrollIntoView({
behavior:"smooth" // !
})
這個參數的作用就是“平滑滾動”!不過由于新是以相容性目前還不算很好,但是也無妨,畢竟它原本的作用還在,可以把新特性當做“隻對少數使用者更新的體驗”。
說完了錨點效果,來說說頁面中普遍存在的“回到頂部”。
同樣的,之前筆者做社團官網的時候就采用了控制
scrollTop
不斷
--
的方法。現在看來太麻煩了,用上面提到的
scrollIntoView()
API就可完成:
目前大部分官網/網站都采用了在頁面頂部(左上角)放一張logo的形式。針對使用這種形式的網站,若是logo所在div不是采用
position:fixed;
的方式固定在頂部的話,我們可以将img外面包裹一層a标簽,然後就可以在“回到頂部”按鈕的事件中這麼寫:
document.links[0].scrollIntoView({
behavior:"smooth"
})
//或者 直接定位圖檔
document.images[0].scrollIntoView({behavior:"smooth"})
這裡還要提到js中後來新加入的兩個原生API:
scrollTo
(滾動到哪裡) 和
scrollBy
(滾動多少距離)。這裡為啥提到這倆?因為scrillTo這個API可以在ie上使用!它被支援在window上,比如:回到頂部
window.scrollTo(0,0);
但這裡也隻是提一句讓各位有個印象,非要用在本文所提場景下則大可不必。
說到這,前幾天驚奇的發現CSS中多了一個控制頁面滾動的屬性!(去年年底開始支援的)原生平滑滾動定位 ——
scroll-behavior
,它有兩個值:
- auto:初始值。啥也沒有。
- smooth:作用在滾動容器元素上,可以讓容器内的滾動變得平滑。
比如在筆者文章中多次出現的tab框切換,如果你隻設定了父容器的
overflow:hidden;
而子元素采用了相對父元素定位的方式展示(display、opacity、visibility都不行,因為他們本質上都不是通過位置移動展示元素的),那麼就可以在父容器上加上一行:
scroll-behavior: smooth;
效果賊好,生動了許多!
還有上面說的“回到頂部”(和“錨點”),如果不想用js實作而且又不抗拒url後面的小小“#”的話,其實你可以這麼實作:
/** css樣式 */
html, body { scroll-behavior:smooth; }
<a href="#">傳回頂部</a>
包括上面的“錨點效果”也可以這麼來~