当前位置: 首页 > news >正文

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

目录

1. 效果展示

2. 效果分析

2.1 关键点

2.2 实现方法

3. 代码实现

3.1 html部分

3.2 css部分

3.3 js部分 

3.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博客

相关文章:

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

目录 1. 效果展示 2. 效果分析 2.1 关键点 2.2 实现方法 3. 代码实现 3.1 html部分 3.2 css部分 3.3 js部分 3.4 完整代码 4. 总结 1. 效果展示 如图所示&#xff0c;页面左侧有一个包含不同课程&#xff08;如语文、数学等&#xff09;的列表&#xff0c;页面右侧…...

C#使用OpenTK绘制3D可拖动旋转图形三棱锥

接上篇,绘制着色矩形 C#使用OpenTK绘制一个着色矩形-CSDN博客 上一篇安装OpenTK.GLControl后,这里可以直接拖动控件GLControl 我们会发现GLControl继承于UserControl //// 摘要:// OpenGL-aware WinForms control. The WinForms designer will always call the default//…...

排序的本质、数据类型及算法选择

排序的本质、数据类型及算法选择 一、排序的本质二、排序的数据类型三、排序算法的选择依据 前两天老金写了篇 “十大排序简介”&#xff0c;有点意犹未尽&#xff0c;这一回老金想把排序连根拔起&#xff0c;从排序的本质说道说道。 一、排序的本质 从字面上理解&#xff0c…...

Python的列表基础知识点(超详细流程)

目录 一、环境搭建 二、列表 2.1 详情 2.2 列表定义 2.3 列表长度 2.4 列表索引 2.5 切片索引 2.6 添加 2.7 插入 2.8 剔除 2.8.1 pop方法 2.8.2 del方法 2.9 任何数据类型 2.10 拼接 2.10.1 “” 2.10.2 “*” 2.11 逆序 ​编辑 2.12 计算出现次数 2.13 排序…...

HarmonyOS鸿蒙开发 弹窗及加载中指示器HUD功能实现

HarmonyOS鸿蒙开发 弹窗及加载中指示器HUD功能实现 最近在学习鸿蒙开发过程中&#xff0c;阅读了官方文档&#xff0c;在之前做flutter时候&#xff0c;经常使用overlay&#xff0c;使用OverlayEntry加入到overlayState来做添加悬浮按钮、提示弹窗、加载中指示器、加载失败的t…...

【Ubuntu与Linux操作系统:一、Ubuntu安装与基本使用】

第1章 Ubuntu安装与基本使用 1.1 Linux与Ubuntu Linux是一种开源、类Unix操作系统内核&#xff0c;拥有高稳定性和强大的网络功能。由于其开源性和灵活性&#xff0c;Linux被广泛应用于服务器、嵌入式设备以及桌面环境中。 Ubuntu是基于Debian的一个流行Linux发行版&#xf…...

React 元素渲染

React 元素渲染 React 是一个用于构建用户界面的 JavaScript 库&#xff0c;它允许开发人员创建大型应用程序&#xff0c;这些应用程序可以随着时间的推移而高效地更新和渲染。React 的核心概念之一是元素渲染&#xff0c;它描述了如何将 JavaScript 对象转换为 DOM&#xff0…...

【2024年华为OD机试】 (C卷,100分)- 括号匹配(Java JS PythonC/C++)

一、问题描述 题目描述 给定一个字符串&#xff0c;里边可能包含“()”、“[]”、“{}”三种括号&#xff0c;请编写程序检查该字符串中的括号是否成对出现&#xff0c;且嵌套关系正确。 若括号成对出现且嵌套关系正确&#xff0c;或该字符串中无括号字符&#xff0c;输出&am…...

解锁企业数字化转型新力量:OpenCoze(开源扣子)

在当今数字化浪潮席卷之下&#xff0c;企业对于高效管理和协同运作的需求愈发迫切&#xff0c;而开源技术正逐渐成为众多企业破局的关键利器。今天&#xff0c;想给大家介绍一款极具潜力的开源项目 ——OpenCoze&#xff0c;中文名称 “开源扣子”。 一、OpenCoze 是什么&…...

【网络云SRE运维开发】2025第2周-每日【2025/01/12】小测-【第12章 rip路由协议】理论和实操考试题解析

文章目录 选择题答案及解析理论题答案及解析实操题答案及解析下一步进阶 选择题答案及解析 RIP路由协议是基于哪种算法的动态路由协议&#xff1f; 答案&#xff1a;B. 距离矢量算法解析&#xff1a;链路状态算法用于OSPF等协议&#xff1b;最小生成树算法主要用于生成树协议&…...

【微服务】8、分布式事务 ( XA 和 AT )

文章目录 利用Seata解决分布式事务问题&#xff08;XA模式&#xff09;AT模式1. AT模式原理引入2. AT模式执行流程与XA模式对比3. AT模式性能优势及潜在问题4. AT模式数据一致性解决方案5. AT模式一阶段操作总结6. AT模式二阶段操作分析7. AT模式整体特点8. AT模式与XA模式对比…...

CVE-2025-22777 (CVSS 9.8):WordPress | GiveWP 插件的严重漏洞

漏洞描述 GiveWP 插件中发现了一个严重漏洞&#xff0c;该插件是 WordPress 最广泛使用的在线捐赠和筹款工具之一。该漏洞的编号为 CVE-2025-22777&#xff0c;CVSS 评分为 9.8&#xff0c;表明其严重性。 GiveWP 插件拥有超过 100,000 个活跃安装&#xff0c;为全球无数捐赠平…...

TypeScript Jest 单元测试 搭建

NPM TypeScript 项目搭建 创建目录 mkdir mockprojectcd mockproject初始化NPM项目 npm init -y安装TypeScript npm i -D typescript使用VSCode 打开项目 创建TS配置文件tsconfig.json {"compilerOptions": {"target": "es5","module&…...

基于 SSH 的任务调度系统

文末附有完整项目代码 在当今科技飞速发展的时代&#xff0c;任务调度系统的重要性日益凸显。本文将详细介绍一个基于 SSH&#xff08;SpringStruts2Hibernate&#xff09;的任务调度系统的设计与实现。 一、系统概述 本系统旨在改变传统人工任务调度方式&#xff0c;通过计算…...

filestream安装使用全套+filebeat的模块用法

1 filestream介绍 官方宣布&#xff1a;输入类型为log在filebeat7.16版本已经弃用了 Filestream 是 Filebeat 中的一种 输入类型&#xff08;Input&#xff09;&#xff0c;用于处理日志文件的读取。它是为了取代 Filebeat 中传统的 log 输入&#xff08;Input&#xff09;设…...

java项目之房屋租赁系统源码(springboot+mysql+vue)

项目简介 房屋租赁系统实现了以下功能&#xff1a; 房屋租赁系统的主要使用者分为&#xff1a; 系统管理&#xff1a;个人中心、房屋信息管理、预约看房管理、合同信息管理、房屋报修管理、维修处理管理、房屋评价管理等模块的查看及相应操作&#xff1b; 房屋信息管理&#…...

sap mm学习笔记

1. 业务流程 2. 组织架构 3. 物料主数据 4.采购主数据 5. 采购管理 6. 库存管理 7.物料主数据 8. 采购申请 ME51N...

代码随想录_链表

代码随想录02 链表 203.移除链表元素 力扣题目链接(opens new window) 题意&#xff1a;删除链表中等于给定值 val 的所有节点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5] 示例 2&#xff1a; 输入&#xff1a;he…...

EF Code 并发控制

【悲观控制】 不推荐用&#xff0c;EF Core 没有封装悲观并发控制的使用&#xff0c;需要使用原生Sql来使用悲观并发控制 一般使用行锁、表锁等排他锁对资源进行锁定&#xff0c;同时只有一个使用者操作被锁定的资源 拿sql server举例&#xff0c;可以使用表所、或者行所解决…...

ceph fs status 输出详解

ceph fs status 命令用于显示 Ceph 文件系统的状态信息&#xff0c;其中各列的含义如下&#xff1a; RANK&#xff1a;元数据服务器&#xff08;MDS&#xff09;的等级或标识符。 STATE&#xff1a;MDS 的当前状态&#xff0c;例如 active&#xff08;活跃&#xff09;、stan…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...