在前端開(kāi)發(fā)中,頁(yè)面中的數(shù)據(jù)元素橫向排列是一種很常見(jiàn)的ui設(shè)計(jì),比如首頁(yè)面中的產(chǎn)品列表,tab標(biāo)簽頁(yè)的標(biāo)題等。
當(dāng)列表元素?cái)?shù)量過(guò)多的時(shí)候,橫向排列不下,就會(huì)出現(xiàn)橫向滾動(dòng)條,或者讓列表元素?fù)Q行的情況。
今天我們介紹一種可以讓列表元素左右拖拽的實(shí)現(xiàn)方案,以避免出現(xiàn)橫向滾動(dòng)條,和換行的情況。
最為演示示例,我們假定頁(yè)面中有一個(gè) div 作為數(shù)據(jù)顯示的容器(container),container 內(nèi)有一個(gè)子元素(div),子元素的寬度大于container,我們?cè)O(shè)置container 的css 樣式:overflow: hidden; 如下圖:
現(xiàn)在,我們?cè)谧釉刂刑砑訑?shù)據(jù)列表:
實(shí)現(xiàn)代碼:
HTML
<div class="container"> <div class="data-list"> <div class="data-item">數(shù)據(jù)項(xiàng) 1</div> <div class="data-item">數(shù)據(jù)項(xiàng) 2</div> <div class="data-item">數(shù)據(jù)項(xiàng) 3</div> <div class="data-item">數(shù)據(jù)項(xiàng) 4</div> <div class="data-item">數(shù)據(jù)項(xiàng) 5</div> <div class="data-item">數(shù)據(jù)項(xiàng) 6</div> <div class="data-item">數(shù)據(jù)項(xiàng) 7</div> <div class="data-item">數(shù)據(jù)項(xiàng) 8</div> <div class="data-item">數(shù)據(jù)項(xiàng) 9</div> <div class="data-item">數(shù)據(jù)項(xiàng) 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;}
接下來(lái),我們給子元素(data-list)添加拖動(dòng)事件。我們先前寫(xiě)過(guò)一篇關(guān)于拖動(dòng)div的文章(JS 實(shí)現(xiàn) div 自由拖拽),大家有興趣的可以看一下。在那片文章中實(shí)現(xiàn)的是鼠標(biāo)自由拖拽div,本章中只需要左右拖動(dòng)。另外,為了多演示一種拖動(dòng)方式,本章中我們使用 transform 方式來(lái)實(shí)現(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)';}
至此,子元素就實(shí)現(xiàn)了左右拖動(dòng):
不過(guò)還有個(gè)問(wèn)題,就是在子元素可以被無(wú)限橫向拖拽,甚至可以被拖到視窗范圍之外。所以我們還需要在結(jié)束拖拽的時(shí)候,也就是 mouseup 事件中,檢查一下子元素的位置,如果子元素的最左側(cè)與容器的最左側(cè)有間距,或者子元素的最右側(cè)與容器的最右側(cè)有間距,就重新調(diào)整子元素的偏移量。
為此,我們添加一個(gè) 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); }}
同時(shí),修改一下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)子元素被拖到視窗之外時(shí),會(huì)自動(dòng)“復(fù)位”:
為了讓子元素在“復(fù)位”的時(shí)候,更平滑一些,我們還可以再做一些改進(jìn),就是給子元素添加動(dòng)畫(huà)(transition)。但是我們不希望在拖動(dòng)的過(guò)程中啟用動(dòng)畫(huà),而是在拖拽結(jié)束,讓子元素恢復(fù)位置的時(shí)候再啟用動(dòng)畫(huà),所以我們?cè)?mouseup 事件中添加 transition 樣式,而在 mousedown 事件的時(shí)候,取消 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ù)項(xiàng) 1</div> <div class="data-item">數(shù)據(jù)項(xiàng) 2</div> <div class="data-item">數(shù)據(jù)項(xiàng) 3</div> <div class="data-item">數(shù)據(jù)項(xiàng) 4</div> <div class="data-item">數(shù)據(jù)項(xiàng) 5</div> <div class="data-item">數(shù)據(jù)項(xiàng) 6</div> <div class="data-item">數(shù)據(jù)項(xiàng) 7</div> <div class="data-item">數(shù)據(jù)項(xiàng) 8</div> <div class="data-item">數(shù)據(jù)項(xiàng) 9</div> <div class="data-item">數(shù)據(jù)項(xiàng) 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 編輯過(guò)