dom元素滾動條滾動控制詳解

前言

不知道你們有沒有遇到過這樣的需求,在某個dom元素中添加新的子元素,而後要求若是新添加的新元素超出容器的範圍,那麼咱們須要自動滾動到新添加的子元素的位置,以下圖所示效果:javascript

那麼接下來咱們一邊學習一些dom元素滾動相關的知識點,一邊實現一個上圖的效果和一些其餘滾動相關的功能。css

須要瞭解的dom屬性和方法

scrollTop、clientHeight和scrollHeight

scrollTop屬性是一個描述容器元素內容的top值與容器元素(viewport)視口頂部top值之間的差值,即容器中內容向上滑動後超出容器視口的部分。能夠經過修改此屬性控制滾動狀態。html

clientHeight是描述容器高度的dom屬性。java

scrollHeight是描述容器內容高度的dom屬性。閉包

三個屬性的關係以下圖所示:dom

關係圖

getBoundingClientRect()

此方法用來獲取元素佈局所需的一些幾何屬性,好比leftrighttopbottomheightwidth等。函數

srollBy(x,y)

dom容器的scrollTo方法能夠用來直接控制滾動條滾動指定的距離。當須要滾動到指定元素時,使用此方法比較方便。佈局

srollTo(x,y)

dom容器的scrollTo方法能夠用來直接控制滾動條滾動到指定位置。在控制滾動條滾動到頂部或者底部的時候使用此方法比較方便。學習

實現滾動控制

準備

咱們先準備一個htmlflex

<!DOCTYPE html>
<html>
    <head>
       <title>滾動條設置詳解</title>
       <style> #scroll_container{ height: 500px; width: 500px; overflow-y: scroll; padding: 50px; box-sizing: border-box; } .scroll_item{ height: 200px; width: 500px; margin-top: 20px; background-color: aquamarine; display: flex; align-items: center; justify-content: center; } </style>
    </head>
    <body>
       <div id="scroll_container">
           <div id="scroll_container">
               <div id="item1" class="scroll_item">
                   <span>1</span>
               </div>
               <div id="item2" class="scroll_item">
                    <span>2</span>
                </div>
                <div id="item3" class="scroll_item">
                    <span>3</span>
                </div>
                <div id="item4" class="scroll_item">
                    <span>4</span>
                </div>
                <div id="item5" class="scroll_item">
                    <span>5</span>
                </div> 
           </div>
           <button onclick="addItem()">添加一個元素</button>
       </div>
    </body>
    <script> let container=document.getElementById("scroll_container"); let index=5; //添加一個元素 function addItem(){ index+=1; let item=`<div id="${'item'+index}" class="scroll_item"> <span>${index}</span> </div>`; container.innerHTML+=item; setTimeout(()=>{ scrollToIndex(); }) } </script>
</html>
複製代碼

上面的代碼包含一個可滾動的區域,並能夠爲滾動區域添加元素,也能夠滾動到指定的元素位置,大體效果以下圖。

使用scrollTop實現

基礎實現

以前已經說明過scrollTop的含義,咱們能夠經過修改容器元素scrollTop值來控制滾動條滾動。scrollTop的值越大,滾動條相對於原始狀態(scrollTop爲0時)的滾動距離越大。

瞭解了scrollTop的含義,咱們就能夠利用scrollTop來實現滾動條的控制,那麼咱們先實現一個滾動到底部的實現,爲上面的代碼添加一個scrollToBottom()的方法:

function scrollToBottom(){
    let y=container.scrollHeight-container.clientHeight;
    container.scrollTop=y;
}
複製代碼

對應的若是想要實現滾動到頂部咱們只須要設置scrollTop爲0便可:

function scrollToTop(){
    container.scrollTop=0;
}
複製代碼

結合getBoundingClientRect()方法咱們也能夠輕鬆實現滾動到指定元素,其中getBoundingClientRect().top表示子元素頂部距離父元素視口頂部的距離:

function scrollToElement(el){
     container.scrollTop+=el.getBoundingClientRect().top;
}
複製代碼

添加動畫

  • 滾動到底部
    可是上面代碼的滾動未免太生硬了,咱們能夠爲它添加一下動畫效果,能夠藉助setInterval()實現一下。分析一下實現動畫效果的過程,動畫的實現無外乎是把一個變量的變化在必定的時間內完成,所以咱們首先須要知道兩個變量,變量(scrollTop)偏移量和變化所需時間,而偏移量就是scrollTop的最終值減去原始值,變化時長通常設置成能夠修改的參數。瞭解了以上過程,咱們先以滾動到底部爲例:
//首先編寫一個scrollToBottom函數
function scrollToBottom(el){
              if(!el){
                  el=container;
              }
              //原始值
              let startTop=el.scrollTop;
              //最終值
              let endTop=el.scrollHeight-el.clientHeight;
              //生成一個動畫控制函數
              let scrollAnimationFn=doAnimation(startTop,endTop,300,el);
              //執行動畫,每10ms執行一次
              let interval=setInterval(()=>{
                scrollAnimationFn(interval)
              },10)
 }
/** * @description: 一個生成動畫控制函數的工廠函數(使用閉包) * @param { startValue:變量原始值 endValue:變量最終值 duration:動畫時長 el:執行滾動動畫的元素 } * @return: null */
function doAnimation(startValue,endValue,duration,el){
              //使用閉包保存變量dy和step(每次動畫滾動的距離)
              let dy=0;
              let step=(endValue-startValue)/(duration/10);
              //返回動畫控制函數
              return function(interval){
                  dy+=step;
                  if(dy>=endValue-startValue){
                      clearInterval(interval);
                  }
                  el.scrollTop+=step;
              }
 }
複製代碼

修改addItem函數添加滾動到底部動畫:

function addItem(){
            index+=1;
            let item=`<div id="${'item'+index}" class="scroll_item"> <span>${index}</span> </div>`;
            container.innerHTML+=item;  
            setTimeout(()=>{
                // scrollToIndex();
                scrollToBottom(container);
            })
           
 }
複製代碼

而後爲html加入一個滾動到底部的按鈕:

<button onclick="scrollToBottom()">滾動到底部</button>
複製代碼

  • 滾動到頂部
    按照上面的方法也能夠實現一個經常使用的帶動畫滾動到頂部:
//編寫一個scrollToTop函數
function scrollToTop(el){
              if(!el){
                  el=container;
              }
              //原始值
              let startTop=el.scrollTop;
              //最終值
              let endTop=0;
              //生成一個動畫控制函數
              let scrollAnimationFn=doAnimation(startTop,endTop,300,el);
              //執行動畫,每10ms執行一次
              let interval=setInterval(()=>{
                scrollAnimationFn(interval)
              },10)
 }
複製代碼

爲了適配滾動到底部咱們須要修改一下動畫中止的時機判斷,修改後的doAnimation()函數以下:

function doAnimation(startValue,endValue,duration,el){
              //使用閉包保存變量dy和step(每次動畫滾動的距離)
              let dy=0;
              let step=(endValue-startValue)/(duration/10);
              return function(interval){
                  dy+=step;
                  //這裏改爲使用絕對值判斷
                  if(Math.abs(dy)>=Math.abs(endValue-startValue)){
                      clearInterval(interval);
                  }
                  el.scrollTop+=step;
              }
 }
複製代碼

最後咱們再給html添加一個滾動到底部按鈕:

<button onclick="scrollToTop()">滾動到頂部</button>
複製代碼

實現效果以下圖:

  • 滾動到指定元素
    首先爲html元素添加所需的按鈕和輸入框:
<input type="number" placeholder="請輸入要滾動到的元素index" style="width: 200px;"/>
<button onclick="scrollToElement()">滾動到指定元素</button>
複製代碼

添加一個滾動指定元素的動畫執行函數:

function scrollToElement(containerEl,el){
            if(!containerEl){
                //父元素
                containerEl=container;
            }
            if(!el){
                //獲取到要滾動到的元素
                let input=document.getElementsByTagName('input')[0];
                let id='item'+input.value;
                if(!input.value){
                    id='item'+index;
                }
                el=document.getElementById(id);
            }
            let startTop=containerEl.scrollTop;
            let endTop=startTop+el.getBoundingClientRect().top;
            let scrollAnimationFn=doAnimation(startTop,endTop,300,containerEl);
            let interval=setInterval(()=>{
                scrollAnimationFn(interval)
            },10)
}
複製代碼

實現效果以下:

使用scrollTo()實現

scrollTo(x,y)的使用方法與scrollTop屬性的使用方法基本一致,父元素的scrollTo()方法能夠控制滾動條滾動到指定位置,實際上至關於設置scrollTop的值。舉個例子說明一下:

//這裏以y軸滾動爲例
element.scrollTo(0,y);
element.scrollTop=y;
//上面兩句的效果相同。
複製代碼

因此,使用scrollTo()方法控制滾動條與使用scrollTop基本一致,咱們只須要簡單修改doAnimation()函數,代碼以下:

function doAnimation(startValue,endValue,duration,el){
              //使用閉包保存變量dy和step(每次動畫滾動的距離)
              let dy=0;
              let step=(endValue-startValue)/(duration/10);
              return function(interval){
                  dy+=step;
                  if(Math.abs(dy)>=Math.abs(endValue-startValue)){
                      clearInterval(interval);
                  }
                  //el.scrollTop+=step;//這行代碼修改成以下
                  el.scrollTo(0,el.scrollTop+step);
              }
}
複製代碼

執行效果與使用scrollTop實現一致。

使用scrollBy()實現

基礎實現

咱們一樣可使用scrollBy(x,y)實現對滾動條的控制,上面已經說明過,scrollBy()方法是控制滾動條滾動指定距離(注意不是位置)。使用scrollBy()能夠很方便的實現滾動到指定元素的需求,代碼以下:

function scrollToElement(containerEl,el){
    //由於getBoundingClientRect().top即爲子元素頂部距離父元素頂部的距離,因此這個值就是子元素相對於父元素的偏移量,咱們傳入這個值到scrollBy中,即滾動到指定元素
    containerEl.scrollBy(0,el.getBoundingClientRect().top);
}
複製代碼

滾動到底部:

function scrollToBottom(containerEl){
    let dy=containerEl.scrollHeight-containerEl.clientHeight;
    containerEl.scrollBy(0,dy);
}
複製代碼

滾動到頂部

function scrollToTop(containerEl){
    let dy=-(containerEl.scrollHeight-containerEl.clientHeight);
    containerEl.scrollBy(0,dy);
}
複製代碼

添加動畫

這裏咱們修改一下動畫生成的函數,由於這裏咱們scrollBy()的參數就是變量的偏移量,因此作出以下修改:

function scrollToBottom(containerEl){
              if(!containerEl){
                containerEl=container;
              }
              //dy即爲偏移量
              let dy=containerEl.scrollHeight-containerEl.clientHeight;
              let scrollAnimationFn=doAnimation(dy,300,containerEl);
              let interval=setInterval(()=>{
                scrollAnimationFn(interval)
              },10)
         }
         function scrollToTop(containerEl){
              if(!containerEl){
                containerEl=container;
              }
              //dy即爲偏移量
              let dy=-(containerEl.scrollHeight-containerEl.clientHeight);
              let scrollAnimationFn=doAnimation(dy,300,containerEl);
              let interval=setInterval(()=>{
                scrollAnimationFn(interval)
              },10)
         }
         function scrollToElement(containerEl,el){
            if(!containerEl){
                containerEl=container;
            }
            if(!el){
                let input=document.getElementsByTagName('input')[0];
                let id='item'+input.value;
                if(!input.value){
                    id='item'+index;
                }
                el=document.getElementById(id);
            }
           //dy即爲偏移量
            let dy=el.getBoundingClientRect().top;
            let scrollAnimationFn=doAnimation(dy,300,containerEl);
            let interval=setInterval(()=>{
                scrollAnimationFn(interval)
            },10)
         }
         /** * @description: * @param {type} * @return: */
         function doAnimation(dy,duration,el){
              //使用閉包保存變量exe_dy和step等變量(每次動畫滾動的距離)
              let exe_dy=0;//已經執行的偏移量
              let step=dy/(duration/10);
              return function(interval){
                  exe_dy+=step;
                  if(Math.abs(exe_dy)>=Math.abs(dy)){
                      clearInterval(interval);
                  }
                  el.scrollBy(0,step);
              }
         }
複製代碼

執行效果與使用scrollTop實現一致。

最後

以上👆就是本身對dom滾動條控制的詳細總結和講解,以及一些基本使用方法。若是錯誤還請指正。

若是以爲能夠的話,歡迎點贊o( ̄▽ ̄)d~
相關文章
相關標籤/搜索