在前端開發(fā)中,頁面中的數(shù)據(jù)元素橫向排列是一種很常見的ui設(shè)計,比如首頁面中的產(chǎn)品列表,tab標(biāo)簽頁的標(biāo)題等。
當(dāng)列表元素數(shù)量過多的時候,橫向排列不下,就會出現(xiàn)橫向滾動條,或者讓列表元素?fù)Q行的情況。
今天我們介紹一種可以讓列表元素左右拖拽的實現(xiàn)方案,以避免出現(xiàn)橫向滾動條,和換行的情況。
最為演示示例,我們假定頁面中有一個 div 作為數(shù)據(jù)顯示的容器(container),container 內(nèi)有一個子元素(div),子元素的寬度大于container,我們設(shè)置container 的css 樣式:overflow: hidden; 如下圖:
現(xiàn)在,我們在子元素中添加數(shù)據(jù)列表:
實現(xiàn)代碼:
HTML
<div class="container">
<div class="data-list">
<div class="data-item">數(shù)據(jù)項 1</div>
<div class="data-item">數(shù)據(jù)項 2</div>
<div class="data-item">數(shù)據(jù)項 3</div>
<div class="data-item">數(shù)據(jù)項 4</div>
<div class="data-item">數(shù)據(jù)項 5</div>
<div class="data-item">數(shù)據(jù)項 6</div>
<div class="data-item">數(shù)據(jù)項 7</div>
<div class="data-item">數(shù)據(jù)項 8</div>
<div class="data-item">數(shù)據(jù)項 9</div>
<div class="data-item">數(shù)據(jù)項 10</div>
</div>
</div>
CSS
.container {
height: 100vh;
width: 600px;
background-color: #fff;
border: 1px solid #ddd;
overflow: hidden;
}
.data-list {
height: 100px;
width: 2090px;
}
.data-item {
float: left;
height: 100%;
width: 200px;
background-color: #eee;
text-align: center;
line-height: 100px;
user-select: none;
}
.data-item + .data-item {
margin-left: 10px;
}
接下來,我們給子元素(data-list)添加拖動事件。我們先前寫過一篇關(guān)于拖動div的文章(JS 實現(xiàn) div 自由拖拽),大家有興趣的可以看一下。在那片文章中實現(xiàn)的是鼠標(biāo)自由拖拽div,本章中只需要左右拖動。另外,為了多演示一種拖動方式,本章中我們使用 transform 方式來實現(xiàn)。如下 JS 代碼:
var el = document.querySelector('.data-list');
el.addEventListener('mousedown', drag);
var offset=0;
function drag(e){
var startX = e.clientX;
var mousemove=(evt)=>{
let x=evt.clientX;
offset += x - startX;
setPosition(offset);
startX=x;
};
var mouseup=(evt)=>{
document.removeEventListener('mousemove', mousemove);
document.removeEventListener('mouseup', mouseup);
};
document.addEventListener('mousemove', mousemove);
document.addEventListener('mouseup', mouseup);
}
function setPosition(ofs){
el.style.transform='translateX('+ofs+'px)';
}
至此,子元素就實現(xiàn)了左右拖動:
不過還有個問題,就是在子元素可以被無限橫向拖拽,甚至可以被拖到視窗范圍之外。所以我們還需要在結(jié)束拖拽的時候,也就是 mouseup 事件中,檢查一下子元素的位置,如果子元素的最左側(cè)與容器的最左側(cè)有間距,或者子元素的最右側(cè)與容器的最右側(cè)有間距,就重新調(diào)整子元素的偏移量。
為此,我們添加一個 checkPosition 的函數(shù):
function checkPosition(){
if(offset>0){
this.setPosition(0);
return;
}
var maxOffset=el.offsetWidth - el.parentNode.offsetWidth;
if(Math.abs(offset)>maxOffset){
setPosition(-maxOffset);
}
}
同時,修改一下setPosition 函數(shù),以更新偏移量:
function setPosition(ofs){
el.style.transform='translateX('+ofs+'px)';
offset=ofs;
}
然后在mouseUp事件中,調(diào)用 checkPosition:
var mouseup=(evt)=>{
checkPosition();
document.removeEventListener('mousemove', mousemove);
document.removeEventListener('mouseup', mouseup);
};
這樣當(dāng)子元素被拖到視窗之外時,會自動“復(fù)位”:
為了讓子元素在“復(fù)位”的時候,更平滑一些,我們還可以再做一些改進(jìn),就是給子元素添加動畫(transition)。但是我們不希望在拖動的過程中啟用動畫,而是在拖拽結(jié)束,讓子元素恢復(fù)位置的時候再啟用動畫,所以我們在 mouseup 事件中添加 transition 樣式,而在 mousedown 事件的時候,取消 transition 樣式。如下:
…
var offset=0;
function drag(e){
el.style.transition='';
var startX = e.clientX;
…
function checkPosition(){
el.style.transition='transform 0.3s';
if(offset>0){
this.setPosition(0);
return;
…
}
…
完整代碼如下:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<title>DIV 拖拽</title>
<style>
.container {
height: 100vh;
width: 600px;
background-color: #fff;
border: 1px solid #ddd;
overflow: hidden;
}
.data-list {
height: 100px;
width: 2090px;
}
.data-item {
float: left;
height: 100%;
width: 200px;
background-color: #eee;
text-align: center;
line-height: 100px;
user-select: none;
}
.data-item + .data-item {
margin-left: 10px;
}
</style>
</style>
</head>
<body>
<div class="container">
<div class="data-list">
<div class="data-item">數(shù)據(jù)項 1</div>
<div class="data-item">數(shù)據(jù)項 2</div>
<div class="data-item">數(shù)據(jù)項 3</div>
<div class="data-item">數(shù)據(jù)項 4</div>
<div class="data-item">數(shù)據(jù)項 5</div>
<div class="data-item">數(shù)據(jù)項 6</div>
<div class="data-item">數(shù)據(jù)項 7</div>
<div class="data-item">數(shù)據(jù)項 8</div>
<div class="data-item">數(shù)據(jù)項 9</div>
<div class="data-item">數(shù)據(jù)項 10</div>
</div>
</div>
<script>
var el = document.querySelector('.data-list');
el.addEventListener('mousedown', drag);
var offset=0;
function drag(e){
el.style.transition='';
var startX = e.clientX;
var mousemove=(evt)=>{
let x=evt.clientX;
offset += x - startX;
setPosition(offset);
startX=x;
};
var mouseup=(evt)=>{
checkPosition();
document.removeEventListener('mousemove', mousemove);
document.removeEventListener('mouseup', mouseup);
};
document.addEventListener('mousemove', mousemove);
document.addEventListener('mouseup', mouseup);
}
function setPosition(ofs){
el.style.transform='translateX('+ofs+'px)';
offset=ofs;
}
function checkPosition(){
el.style.transition='transform 0.3s';
if(offset>0){
this.setPosition(0);
return;
}
var maxOffset=el.offsetWidth - el.parentNode.offsetWidth;
if(Math.abs(offset)>maxOffset){
setPosition(-maxOffset);
}
}
</script>
</body>
</html>
該文章在 2025/7/1 21:09:46 編輯過