前言:因为做项目时有类似于九宫格展示图片,然后可以点击选择图片,当时提了一下可以做一个鼠标按下拖动多选功能,后来因为其它需求,这个功能被舍弃掉,但我还是研究了一下,这里做了个demo,并添加了拖动排序等功能。
以下是demo效果图,这里是demo地址
1.html结构,css略过
1 | <ul class="list"> |
第一个ul
元素是用来存放待选择元素的,这里面有个div
元素,平常是不可见的,只有鼠标按下时用来显示遮罩层的,效果是这样的
第二个ul
元素是用来存放选中拖拽过来的元素
2. js
首先需要思考个问题,怎么才能得到被遮罩层罩住的元素呢?
最开始我是想用dom自带的 IntersectionObserver
来完成观察,后来没研究出来,最后使用坐标来判断
怎么使用坐标来判断呢?
首先拿到遮罩层mark在屏幕中的位置,因为这个是动态变化并赋值给遮罩层mark,所以在赋值的时候记录一下就可以了,然后获取所有li
的节点信息,然后根据li
的位置信息判断,如果li
节点中的任意位置在遮罩层内部,那么认为该节点被选中,这个根据li
节点的上下左右边框的位置与遮罩层mark的位置比较就能得出,然后将选中的li
节点存放起来并设置一个背景色就可以了。
2.1 拿到遮罩层dom元素,并创建一个markInfo对象,用来存放遮罩层的信息
1 | let maskEl=$('#mask')[0]; |
** 注意:因为框选时可能会存在框选已选中的元素,元素智能被选中一次,因此用直接用set来存放选中的元素集合**
2.2 鼠标右键按下时,遮罩层开始显示,鼠标移动时,根据鼠标移动的距离来计算遮罩层的位置信息,因为这个距离是用鼠标按下的距离减去鼠标抬起的距离,因此有正负之分,刚好可以用来区分当前是要选择框选的元素(maskInfo.isSelectMode=true),还是要取消被框选元素的选择状态(maskInfo.isSelectMode=false)。然后再鼠标抬起时,就可以得到最终的遮罩层信息,用来计算哪些li
被选中了
1 |
|
2.3 找到被选中的li。
怎么找到哪些li
是被选中的呢?这里我想到了几种发生在li
元素上面的情况
- 任意一个角被遮罩层罩住
- 遮罩层在某一个
li
内部 - 只有上边或者下边,或者上下边被罩住的情况
- 只有左边或者右边,或者左右边被罩住的情况
1 | function findSelected(){ |
注意:因为被选中额元素是可以被拖动的,所以要将被选中的元素添draggable属性设置为true,被取消的元素设置为false
这样选择和取消功能就完成了。
3.选中元素的拖拽排序
我们怎么将元素插入到拖动的位置呢?
Node.insertBefore() 可以完美解决这个问题,详情看MDN
** 因此,只需要找到要背插入的元素和要插入到哪个元素之前就可以了 **
3.1 给要拖动的元素li
的父元素绑定拖拽事件
1 | const droppables = document.querySelectorAll('.list'); |
3.2 在拖拽事件中找到要插入的元素和插入元素的位置
1 | function dragOver(e) { |
4.元素拖拽至另一个容器
只需要将拖拽的元素插入到存放的容器即可
1 | function dragDrop(e) { |