手写拖拽排序

579 阅读2分钟

最近学到了一个很好玩的东西,拖拽排序,学会了立马来跟大家分享一下,主要使用到的技术是H5新增的draggable

draggable

介绍:draggable是一个枚举类型的全局属性,用于表示元素是否允许使用拖放操作Api拖动。

最终效果如下

屏幕录制2022-11-16_14_18_08_AdobeExpress.gif

或者

屏幕录制2022-11-16_14_03_48_AdobeExpress.gif

我们来拆分一下代码,首先把html和css搞定

html部分
<ul class="drag-box">
        <li draggable="true" class="drag-item">
            <div class="drag-content">
                <div class="drag-context">1</div>
            </div>
        </li>
        <li draggable="true" class="drag-item">
            <div class="drag-content">
                <div class="drag-context">2</div>
            </div>
        </li>
        <li draggable="true" class="drag-item">
            <div class="drag-content">
                <div class="drag-context">3</div>
            </div>
        </li>
        <li draggable="true" class="drag-item">
            <div class="drag-content">4</div>
        </li>
        <li draggable="true" class="drag-item">
            <div class="drag-content">5</div>
        </li>
        <li draggable="true" class="drag-item">
            <div class="drag-content">6</div>
        </li>
        <!-- <li draggable="true" class="drag-item">
            <div class="drag-content">7</div>
        </li>
        <li draggable="true" class="drag-item">
            <div class="drag-content">8</div>
        </li>
        <li draggable="true" class="drag-item">
            <div class="drag-content">9</div>
        </li> -->
    </ul>

给所有需要进行拖动的元素写上draggable="true",允许拖动,注意是写在拖动部分的最外层容器上。css部分就小伙伴们自己来实现吧。

接下来就是js部分,js部分使用拖放的监听事件来完成:

开始拖动(dragstart)

当用户开始拖动一个元素或者一个选择文本的时候 dragstart 事件就会触发。

我们使用最外层的ul节点来代理拖动开始的dragstart事件

const dragList = document.querySelector(".drag-box"); // 获取拖动区域的容器
let sourceNode = null; // 保存当前拖动的元素
dragList.ondragstart = (e) => {
    setTimeout(() => {
        e.target.classList.add('moving'); // 拖动开始时加上正在拖动元素的样式
    });
    sourceNode = e.target;
};

由于在拖动开始时就添加拖动样式,会导致跟随鼠标拖动的元素的样式改变,比如moving样式为

.drag-item.moving {
    border: 1px dashed black;
}

截屏2022-11-16 14.49.46.png 所以在这里使用异步来添加moving样式

拖动到目标位置时(dragenter)

当拖动的元素或被选择的文本进入有效的放置目标时, dragenter 事件被触发。

由于dragenter事件默认取消拖动,如不清除默认事件,则会发现在拖动结束时拖动的元素会闪回原始位置,所以要取消默认事件

    dragList.ondragenter = (e) => {
        e.preventDefault(); // 清除默认事件
        let target = e.target;
        if(target === sourceNode || target === dragList) { // 放置目标不是自身元素或者最外层容器时有效
            return ;
        }
        // 由于拖动的元素可能(实际上开发场景肯定会有)包含子元素,所以使用while循环将当前元素锁定在拖动元素的最外层容器上
        while(target.parentNode !== dragList) {
            target = target.parentNode;
        };
        const children = Array.from(dragList.children);
        const sourceIndex = children.indexOf(sourceNode); // 获取拖动元素的下标
        const targetIndex = children.indexOf(target); // 获取目标元素的下标
        if(sourceIndex > targetIndex) { // 向上拖动
            dragList.insertBefore(sourceNode, target); // 将拖动元素插入目标元素之前
        } else { // 向下拖动
            // 将拖动元素插入目标元素之后
            dragList.insertBefore(sourceNode, children[targetIndex + 1]);
        }
    };

dragover也有同样的默认事件,所以对dragover也要清除默认事件

dragList.ondragover = (e) => {
    e.preventDefault();
};

拖动结束(dragend)

dragend 事件在拖放操作结束时触发(通过释放鼠标按钮或单击 escape 键)。

dragList.ondragend = (e) => {
    e.target.classList.remove('moving'); // 删除拖动样式
};

至此我们就实现了一个基本的拖拽排序的功能

前端小渣渣的学习总结,如有错误,欢迎指正!!

技术参考:抖音渡一Web前端学习