一、需求背景:
项目要求在多选的情况下对已选择的值进行手动拖动排序
二、设计分析:
我们正常的js插件中没有此功能,需要拖动里面的元素,就需要添加在生成的已选择的li元素里添加h5属性(draggable="true"),这样我们才能有拖动元素的这个效果。然后我们在select初始化的页面再监听拖动事件。除了核心的拖动外,我们还需要注意两件事,一件事是拖动完毕后发送请求后的再次修改操作,这要求返回的值我们要按照顺序显示上去,因为使用过select相关插件的都知道,默认显示值得顺序跟下拉列表的顺序有关系,其次就是如果我们需要根据某些条件需要改变下拉框里的值,需要关注默认值是否在下拉框中,如果没有需要剔除。
三、示意图
四、核心代码
- 我们需要在插件中找到生成已选择值得HTML,加上draggable="true"的属性,让元素可以拖动(这个是我使用插件源代码,在里面添加此属性)
- 添加拖动事件的监控,并写好监控事件的处理逻辑(代码是vue框架,只参照核心js就好了)
this.$nextTick(function () {
//获取ul元素的节点,找到ul li的相关HTML
var node = document.querySelector('.ciphers_ssl .select2-selection__rendered')
var draging = null;
//使用事件委托,将li的事件委托给ul
node.ondragstart = function(event) {
//firefox设置了setData后元素才能拖动!!!!
event.dataTransfer.setData("te", event.target.innerText); //不能使用text,firefox会打开新tab
draging = event.target;
}
node.ondragover = function(event) {
event.preventDefault();
var target = event.target;
//因为dragover会发生在ul上,所以要判断是不是li
if (target.nodeName === "LI"&&target !== draging) {
if (_index(draging) < _index(target)) {
target.parentNode.insertBefore(draging,target.nextSibling);
} else {
target.parentNode.insertBefore(draging, target);
}
}
}
//获取元素在父元素中的index
function _index(el) {
var index = 0;
if (!el || !el.parentNode) {
return -1;
}
//下面这个循环的意思是,看当前el是否有值,并且给当前el赋值上前一个el的值,直到el为null时,循环结束,每一次循环就代表在当前值得前面还有一个,也就直到当前值得序列号
while (el && (el = el.previousElementSibling)) {
index++;
}
return index;
}
})
至于我之前在设计分析提到的两个注意项,只需要参考我另外写的如何初始化select插件就好了,还有就是再次展示的时候,我们需要把已选择的默认值,如果下拉框有,就需要把已选择的值按照顺序放到最前面,这样在初始化的时候,才会按照正确的顺序展示