【前端动效】原生js实现拖拽排课效果

目录

[1. 效果展示](#1. 效果展示)

[2. 效果分析](#2. 效果分析)

[2.1 关键点](#2.1 关键点)

[2.2 实现方法](#2.2 实现方法)

[3. 代码实现](#3. 代码实现)

[3.1 html部分](#3.1 html部分)

[3.2 css部分](#3.2 css部分)

[3.3 js部分](#3.3 js部分)

[3.4 完整代码](#3.4 完整代码)

[4. 总结](#4. 总结)


1. 效果展示

如图所示,页面左侧有一个包含不同课程(如语文、数学等)的列表,页面右侧是一个表格,表示一周内每天的不同时间段。用户可以通过拖拽左侧的课程到右侧的时间表中,来安排课程。

2. 效果分析

**目标:**鼠标摁下左侧某一科目,拖拽到右侧某一位置放下,拖拽的课程就会嵌入到课表框当中

2.1 关键点

(1) 拖拽与放置逻辑的实现

(2) HTML与CSS布局的设计

2.2 实现方法

(1) 将拖拽过程分为 开始拖拽、允许放置、放置三部分分析。

(2) 页面主要由两部分组成------左侧是可拖动的课程项列表,右侧是一个表格,用于显示每周的时间安排。每个课程项都设定了 draggable = "true" 属性,使其可以被拖动。表格中的 <td> 元素(不包括第一列)是可以放置课程的目标区域。

(3) 通过 CSS 确保页面居中显示,并设置了 .box 容器的尺寸和边框。.left 和 .right 分别代表课程项列表和时间表的位置和大小。

3. 代码实现

接下来我会一步一步解说每段关键代码的含义。

3.1 html部分

复制代码
  <div class="box">
        <div class="left">
            <div class="yu" draggable="true">语文</div>
            <div class="shu" draggable="true">数学</div>
            <div class="ying" draggable="true">英语</div>
            <div class="wu" draggable="true">物理</div>
            <div class="hua" draggable="true">化学</div>
            <div class="sheng" draggable="true">生物</div>
        </div>

        <div class="right">
            <table>
                <tr>
                    <th>时间/星期</th>
                    <th>星期一</th>
                    <th>星期二</th>
                    <th>星期三</th>
                    <th>星期四</th>
                    <th>星期五</th>
                </tr>
                <tr>
                    <td>08:00-09:00</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                <tr>
                    <td>10:00-11:00</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                <tr>
                    <td>11:00-12:00</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                <tr>
                    <td>12:00-13:00</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                <tr>
                    <td>14:00-15:00</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                <tr>
                    <td>15:00-16:00</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
            </table>
        </div>
    </div>

左侧是课程列表, 该部分包含六个 <div> 元素,每个元素代表一门课程(如语文、数学等),并赋予了 draggable = "true" 属性,这意味着这些课程项可以被拖动。

右侧是时间表,提供了五个工作日(周一至周五)和六个上课时段的时间框架,使得用户可以直观地安排每周的课程。

3.2 css部分

复制代码
 .left {
            width: 150px;
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            gap: .7rem;
        }
        .left > div {
            width: 100%;
            height: 50px;
            border: 1px solid black;
            text-align: center;
            line-height: 50px;
            font-weight: bold;
            letter-spacing: 4px;
            cursor: move;
        }
        .yu { background: lawngreen; }
        .shu { background: skyblue; }
        .ying { background: mediumslateblue; }
        .wu { background: aqua; }
        .hua { background: violet; }
        .sheng { background: navajowhite; }

        .right {
            width: 750px;
            height: 400px;
            overflow: auto;
        }

css部分没什么好说的,按照自己的喜好随意编写即可。

3.3 js部分

复制代码
const draggables = document.querySelectorAll('.left > div');
const droppables = document.querySelectorAll('.right td:not(:first-child)');

获取所有可拖动的课程项 和 所有可放置课程的时间段单元格(不包括第一列)。

复制代码
draggables.forEach(draggable => {
            draggable.addEventListener('dragstart', e => {
                e.dataTransfer.setData('text/plain', e.target.className);
                e.dataTransfer.dropEffect = 'move';
            });
        });

拖拽开始:: 当用户开始拖动一个课程项时,浏览器触发 dragstart 事件。在这个事件处理函数中,我们使用 setData 方法将被拖拽元素的类名作为数据存储在 Datatransfer 对象中。同时,设置drapEffect 属性为 'move',以表明这是一个移动操作。

复制代码
droppables.forEach(droppable => {
    droppable.addEventListener('dragover', e => {
        e.preventDefault();
        e.dataTransfer.dropEffect = 'move';
    });
});

允许放置: 当拖拽元素经过目标单元格时,浏览器会触发 dragover 事件。默认情况下,浏览器会阻止这个事件,所以我们需要调用 preventDefault() 来允许放置。同样地,我们设置 dropEffect为 'move',以便与 dragstart 中的设置相匹配。

复制代码
droppable.addEventListener('drop', e => {
    e.preventDefault();
    const draggedItemClass = e.dataTransfer.getData('text/plain');
    const draggedItem = document.querySelector(`.${draggedItemClass}`);

    if (e.target.tagName === 'TD') {
        e.target.textContent = draggedItem.textContent;
        e.target.style.backgroundColor = window.getComputedStyle(draggedItem).backgroundColor;
    }
});

放置:当用户释放鼠标按钮,拖拽元素被放置到目标单元格时,浏览器触发 drap 事件。在这个事件处理函数中,我们获取之前存储的数据(即课程项的类名),找到对应的课程项,并将其内容和背景颜色复制到目标单元格中。

3.4 完整代码

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>拖拽实现排课</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background-color: #f0f0f0;
        }
        .box {
            width: 1000px;
            height: 600px;
            border: 1px solid red;
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 20px;
            box-sizing: border-box;
        }
        .left {
            width: 150px;
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            gap: .7rem;
        }
        .left > div {
            width: 100%;
            height: 50px;
            border: 1px solid black;
            text-align: center;
            line-height: 50px;
            font-weight: bold;
            letter-spacing: 4px;
            cursor: move;
        }
        .yu { background: lawngreen; }
        .shu { background: skyblue; }
        .ying { background: mediumslateblue; }
        .wu { background: aqua; }
        .hua { background: violet; }
        .sheng { background: navajowhite; }

        .right {
            width: 750px;
            height: 400px;
            overflow: auto;
        }
        table {
            width: 100%;
            border-collapse: collapse;
        }
        th, td {
            width: 120px;
            height: 50px;
            border: 1px solid black;
            text-align: center;
        }
        td {
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div class="box">
        <div class="left">
            <div class="yu" draggable="true">语文</div>
            <div class="shu" draggable="true">数学</div>
            <div class="ying" draggable="true">英语</div>
            <div class="wu" draggable="true">物理</div>
            <div class="hua" draggable="true">化学</div>
            <div class="sheng" draggable="true">生物</div>
        </div>

        <div class="right">
            <table>
                <tr>
                    <th>时间/星期</th>
                    <th>星期一</th>
                    <th>星期二</th>
                    <th>星期三</th>
                    <th>星期四</th>
                    <th>星期五</th>
                </tr>
                <tr>
                    <td>08:00-09:00</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                <tr>
                    <td>10:00-11:00</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                <tr>
                    <td>11:00-12:00</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                <tr>
                    <td>12:00-13:00</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                <tr>
                    <td>14:00-15:00</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                <tr>
                    <td>15:00-16:00</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
            </table>
        </div>
    </div>

    <script>
        // 获取所有可拖动的课程项
        const draggables = document.querySelectorAll('.left > div');
        // 获取所有可放置课程的时间段单元格(不包括第一列)
        const droppables = document.querySelectorAll('.right td:not(:first-child)');

        // 添加拖拽开始事件监听器
        draggables.forEach(draggable => {
            draggable.addEventListener('dragstart', e => {
                e.dataTransfer.setData('text/plain', e.target.className);
                e.dataTransfer.dropEffect = 'move';
            });
        });

        // 添加拖拽进入和放置事件监听器
        droppables.forEach(droppable => {
            droppable.addEventListener('dragover', e => {
                e.preventDefault();
                e.dataTransfer.dropEffect = 'move';
            });

            droppable.addEventListener('drop', e => {
                e.preventDefault();
                const draggedItemClass = e.dataTransfer.getData('text/plain');
                const draggedItem = document.querySelector(`.${draggedItemClass}`);

                if (e.target.tagName === 'TD') {
                    e.target.textContent = draggedItem.textContent;
                    e.target.style.backgroundColor = window.getComputedStyle(draggedItem).backgroundColor;
                }
            });
        });
    </script>
</body>
</html>

4. 总结

以上即是实现拖拽排课动效的全部内容。如果对您有所帮助,不妨点赞、关注+收藏,下次不迷路😀。

更多前端动效点击下方链接↓ ↓ ↓

前端动效_借来一夜星光的博客-CSDN博客

相关推荐
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅6 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊6 小时前
jwt介绍
前端
爱敲代码的小鱼6 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax