前言
不知道大家有沒有遇到過這樣的需求,在某個dom元素中添加新的子元素,然後要求如果新添加的新元素超出容器的範圍,那麼我們需要自動滾動到新添加的子元素的位置,如下圖所示效果:
那麼接下來我們一邊學習一些dom元素滾動相關的知識點,一邊實現一個上圖的效果和一些其他滾動相關的功能。
需要了解的dom屬性和方法
scrollTop、clientHeight和scrollHeight
scrollTop屬性是一個描述容器元素內容的top值與容器元素(viewport)視口頂部top值之間的差值,即容器中內容向上滑動後超出容器視口的部分。可以通過修改此屬性控制滾動狀態。
clientHeight是描述容器高度的dom屬性。
scrollHeight是描述容器內容高度的dom屬性。
三個屬性的關係如下圖所示:
getBoundingClientRect()
此方法用來獲取元素佈局所需的一些幾何屬性,比如left、right、top、bottom、height、width等。
srollBy(x,y)
dom容器的scrollTo方法可以用來直接控制滾動條滾動指定的距離。當需要滾動到指定元素時,使用此方法比較方便。
srollTo(x,y)
dom容器的scrollTo方法可以用來直接控制滾動條滾動到指定位置。在控制滾動條滾動到頂部或者底部的時候使用此方法比較方便。
實現滾動控制
準備
我們先準備一個html
<!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~