天天看點

聊聊頁面中的錨點效果和回到頂部

頁面中的錨點各位想必都“了如指掌”,其基于a連結“同頁面跳轉”和“id屬性标簽的曆史問題”。

可能有人不知道的是:HTML中的“id”非常神奇:id屬性的元素不僅可以在js中直接使用,而不必先擷取;還可以和url相關聯。

直接用id通路是舊版本js遺留下來的特性,浏覽器會建立window執行個體的id同名屬性,這是為了相容舊的網頁。但不要依賴這個特性,在含有特殊字元或者和window執行個體的其他屬性有沖突時可能失效。

我們都知道,錨點應該這樣用:

<a href="這裡寫#号+id名">去這裡</a>

<div id="上面a中引用的id名">我是目的地</div>      

如此便可。

但是這裡面有兩個小問題:

  1. 使用錨點後url中也會帶有href中的字元串(#号+id名)
  2. 因為是類似跳轉方式,是以過程非常生硬

比如有的網頁中有這樣的效果:點選側邊欄或頂部某一個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行,則頁面滾動行為會發生錯誤。看上去似乎是​​

​clearInterval​

​​失效了!其實這個問題本身是由于js定時器特性産生的。​

​clearInterval​

​是根據定時器本身的辨別來進行清除的,如果在期間生成了新的interval,并覆寫timer辨別對象,舊有的timer定時器對象并不會被停止和清除,而且辨別也會丢失導緻再也無法被清除,是以寫定時器時一定要注意。建議在調用某個定時器任務的函數的時候,一定一開始就把之前的定時器先清除!

這個問題到這裡似乎很好的解決了。但是上面代碼畢竟還是太長了,而且裡面要進行嚴格的判斷以決定開始和結束(定時器的增加和删除),似乎稍有不妥。

不過别急,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​

​ ,它有兩個值:

  1. auto:初始值。啥也沒有。
  2. smooth:作用在滾動容器元素上,可以讓容器内的滾動變得平滑。

比如在筆者文章中多次出現的tab框切換,如果你隻設定了父容器的 ​

​overflow:hidden;​

​​ 而子元素采用了相對父元素定位的方式展示(display、opacity、visibility都不行,因為他們本質上都不是通過位置移動展示元素的),那麼就可以在父容器上加上一行:​

​scroll-behavior: smooth;​

效果賊好,生動了許多!

還有上面說的“回到頂部”(和“錨點”),如果不想用js實作而且又不抗拒url後面的小小“#”的話,其實你可以這麼實作:

/** css樣式 */
html, body { scroll-behavior:smooth; }      
<a href="#">傳回頂部</a>      

包括上面的“錨點效果”也可以這麼來~

繼續閱讀