在前端开发中,页面中的数据元素横向排列是一种很常见的ui设计,比如首页面中的产品列表,tab标签页的标题等。
当列表元素数量过多的时候,横向排列不下,就会出现横向滚动条,或者让列表元素换行的情况。
今天我们介绍一种可以让列表元素左右拖拽的实现方案,以避免出现横向滚动条,和换行的情况。
最为演示示例,我们假定页面中有一个 div 作为数据显示的容器(container),container 内有一个子元素(div),子元素的宽度大于container,我们设置container 的css 样式:overflow: hidden; 如下图:
现在,我们在子元素中添加数据列表:
实现代码:
HTML
<div class="container"> <div class="data-list"> <div class="data-item">数据项 1</div> <div class="data-item">数据项 2</div> <div class="data-item">数据项 3</div> <div class="data-item">数据项 4</div> <div class="data-item">数据项 5</div> <div class="data-item">数据项 6</div> <div class="data-item">数据项 7</div> <div class="data-item">数据项 8</div> <div class="data-item">数据项 9</div> <div class="data-item">数据项 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)添加拖动事件。我们先前写过一篇关于拖动div的文章(JS 实现 div 自由拖拽),大家有兴趣的可以看一下。在那片文章中实现的是鼠标自由拖拽div,本章中只需要左右拖动。另外,为了多演示一种拖动方式,本章中我们使用 transform 方式来实现。如下 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)';}
至此,子元素就实现了左右拖动:
不过还有个问题,就是在子元素可以被无限横向拖拽,甚至可以被拖到视窗范围之外。所以我们还需要在结束拖拽的时候,也就是 mouseup 事件中,检查一下子元素的位置,如果子元素的最左侧与容器的最左侧有间距,或者子元素的最右侧与容器的最右侧有间距,就重新调整子元素的偏移量。
为此,我们添加一个 checkPosition 的函数:
function checkPosition(){ if(offset>0){ this.setPosition(0); return; } var maxOffset=el.offsetWidth - el.parentNode.offsetWidth; if(Math.abs(offset)>maxOffset){ setPosition(-maxOffset); }}
同时,修改一下setPosition 函数,以更新偏移量:
function setPosition(ofs){ el.style.transform='translateX('+ofs+'px)'; offset=ofs;}
然后在mouseUp事件中,调用 checkPosition:
var mouseup=(evt)=>{ checkPosition(); document.removeEventListener('mousemove', mousemove); document.removeEventListener('mouseup', mouseup);};
这样当子元素被拖到视窗之外时,会自动“复位”:
为了让子元素在“复位”的时候,更平滑一些,我们还可以再做一些改进,就是给子元素添加动画(transition)。但是我们不希望在拖动的过程中启用动画,而是在拖拽结束,让子元素恢复位置的时候再启用动画,所以我们在 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">数据项 1</div> <div class="data-item">数据项 2</div> <div class="data-item">数据项 3</div> <div class="data-item">数据项 4</div> <div class="data-item">数据项 5</div> <div class="data-item">数据项 6</div> <div class="data-item">数据项 7</div> <div class="data-item">数据项 8</div> <div class="data-item">数据项 9</div> <div class="data-item">数据项 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 编辑过