HTML5拖拽API学习 托拽排序和可托拽课程表

news2024/11/22 8:18:50

文章目录

      • 前言
      • 拖拽API核心概念
      • 拖拽式使用流程
      • 例子
      • 注意事项
      • 综合例子🌰 可拖拽课程表
      • 拖拽排序

在这里插入图片描述

前言

前端拖拽功能让网页元素可以通过鼠标或触摸操作移动。HTML5 提供了标准的拖拽API,简化了拖放操作的实现。以下是拖拽API的基本使用指南:

拖拽API核心概念

  1. draggable属性:设置元素的draggable="true"属性,允许用户拖动该元素。
    <div draggable="true">可拖动元素</div>
  1. dragstart事件:拖动开始时触发,可以设置拖动数据。
    const draggableElement = document.querySelector('div');
    draggableElement.addEventListener('dragstart', (event) => {
      event.dataTransfer.setData('text/plain', '拖动数据');
 });
  1. dragover事件:拖动元素在目标区域上方时触发,需要调用event.preventDefault()以允许放置。
    const dropZone = document.querySelector('#dropZone');
    dropZone.addEventListener('dragover', (event) => {
      event.preventDefault(); // 允许放置
    });
  1. drop事件:拖动元素放置到目标区域时触发,可以获取拖动数据。
    dropZone.addEventListener('drop', (event) => {
      event.preventDefault();
      const data = event.dataTransfer.getData('text/plain');
      console.log('放置的数据:', data);
    });
  1. dragend事件:拖动操作结束时触发,用于清理拖动状态或重置样式。
draggableElement.addEventListener('dragend', () => {
      draggableElement.style.backgroundColor = ''; // 重置样式
    });

拖拽式使用流程

  1. 设置可拖拽元素:在HTML中为元素添加draggable="true"属性。
  2. 处理拖拽开始事件:在dragstart事件中设置拖拽数据。
  3. 设置目标区域:通过dragover事件处理,允许放置操作。
  4. 处理放置事件:在drop事件中获取数据并处理放置逻辑。
  5. 清理拖拽操作:在dragend事件中清理元素样式或状态。

例子

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>拖拽示例</title>

<style>

#dragElement {

width: 100px; height: 100px; background-color: skyblue; cursor: move; text-align: center; line-height: 100px;

}

#dropZone {

width: 300px; height: 300px; border: 2px dashed #aaa; margin-top: 50px; text-align: center; line-height: 300px; color: #888;

}

</style>

</head>

<body>

<div id="dragElement" draggable="true">拖我</div>

<div id="dropZone">在这里放置</div>

  

<script>

const dragElement = document.getElementById('dragElement');

const dropZone = document.getElementById('dropZone');

  

dragElement.addEventListener('dragstart', (event) => {

console.log('拖拽开始');

event.dataTransfer.setData('text/plain', 'Hello, 拖拽');

event.target.style.backgroundColor = 'orange';

});

  

dropZone.addEventListener('dragover', (event) => {

console.log('拖拽进入');

event.preventDefault();

});

  

dropZone.addEventListener('drop', (event) => {

console.log('拖拽放下');

event.preventDefault();

const data = event.dataTransfer.getData('text/plain');

dropZone.innerHTML = `放置了:${data}`;

});

  

dragElement.addEventListener('dragend', (event) => {

console.log('拖拽结束');

event.target.style.backgroundColor = 'skyblue';

});

</script>

</body>

</html>

注意事项

  • 兼容性:大多数现代浏览器支持HTML5拖拽API,但老旧浏览器如IE8及以下不支持。
  • 样式:可以通过设置样式增强用户体验,如改变光标或透明度。
  • 文件拖放:HTML5还支持拖拽文件到浏览器特定区域,可以通过event.dataTransfer.files获取文件数据。

综合例子🌰 可拖拽课程表

<!DOCTYPE html>

<html lang="zh-CN">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>可拖拽课程表</title>

<style>

body {

margin: 0;

padding: 20px;

}

  

.schedule-container {

display: flex;

gap: 20px;

max-width: 1400px;

margin: 0 auto;

}

.schedule-table {

flex: 1;

display: grid;

grid-template-columns: 100px repeat(7, 1fr);

gap: 2px;

background-color: #fff;

border: 1px solid #ddd;

height: fit-content;

}

  

.time-column {

background-color: #f8f9fa;

padding: 10px;

text-align: center;

font-weight: bold;

}

  

.header-row {

background-color: #f8f9fa;

padding: 10px;

text-align: center;

font-weight: bold;

}

  

.course-list {

width: 250px;

padding: 15px;

border: 1px solid #ddd;

background-color: #f8f9fa;

border-radius: 8px;

height: fit-content;

}

  

.course-list h3 {

margin-top: 0;

margin-bottom: 15px;

color: #333;

}

  

#available-courses {

display: flex;

flex-direction: column;

gap: 10px;

}

</style>

</head>

<body>

<div class="schedule-container">

<div class="schedule-table">

<!-- 表头 -->

<div class="header-row">时间</div>

<div class="header-row">周一</div>

<div class="header-row">周二</div>

<div class="header-row">周三</div>

<div class="header-row">周四</div>

<div class="header-row">周五</div>

<div class="header-row">周六</div>

<div class="header-row">周日</div>

</div>

  

<!-- 课程列表 -->

<div class="course-list">

<h3>可选课程</h3>

<div id="available-courses">

<!-- 这里会通过JavaScript动态生成可拖拽的课程 -->

</div>

</div>

</div>

<script src="tuozuaiApi.js"></script>

</body>

</html>
//tuozhaiApi.js
// 课程表拖拽功能实现

const DragSchedule = {

init() {

this.scheduleTable = document.querySelector('.schedule-table');

  

this.availableCourses = document.getElementById('available-courses');

  

this.createTimeSlots();

  

this.createSampleCourses();

  

this.cells = document.querySelectorAll('.schedule-cell');

  

this.courses = document.querySelectorAll('.course-item');

  

this.bindEvents();

  

this.loadScheduleState();

},

  

createTimeSlots() {

// 创建时间段(第一节课从8:00开始)

const times = [

'8:00-8:45', '8:55-9:40', '9:50-10:35', '10:45-11:30',

'13:30-14:15', '14:25-15:10', '15:20-16:05', '16:15-17:00'

];

  

times.forEach((time, index) => {

// 添加时间列

const timeCell = document.createElement('div');

timeCell.className = 'time-column';

timeCell.textContent = `${index + 1}节\n${time}`;

this.scheduleTable.appendChild(timeCell);

  

// 添加每一天的课程格子

for (let day = 0; day < 7; day++) {

const cell = document.createElement('div');

cell.className = 'schedule-cell';

cell.setAttribute('data-time', index);

cell.setAttribute('data-day', day);

this.scheduleTable.appendChild(cell);

}

});

},

  

createSampleCourses() {

const sampleCourses = [

{ id: 1, name: '高等数学', color: '#ff9999' },

{ id: 2, name: '大学英语', color: '#99ff99' },

{ id: 3, name: '程序设计', color: '#9999ff' },

{ id: 4, name: '物理实验', color: '#ffff99' },

{ id: 5, name: '体育课', color: '#ff99ff' }

];

  

sampleCourses.forEach(course => {

const courseElement = document.createElement('div');

courseElement.className = 'course-item';

courseElement.setAttribute('data-course-id', course.id);

courseElement.setAttribute('draggable', true); // 添加draggable属性

courseElement.textContent = course.name;

courseElement.style.backgroundColor = course.color;

this.availableCourses.appendChild(courseElement);

});

},

  

bindEvents() {

// 为每个课程添加拖拽事件

this.courses.forEach(course => {

course.ondragstart = (e) => {

e.target.classList.add('dragging');

e.dataTransfer.setData('text/plain', e.target.getAttribute('data-course-id'));

};

course.ondragend = (e) => {

e.target.classList.remove('dragging');

};

});

  

// 为每个单元格添加放置事件

this.cells.forEach(cell => {

cell.ondragover = (e) => {

e.preventDefault();

e.currentTarget.classList.add('drag-over');

};

cell.ondragleave = (e) => {

e.currentTarget.classList.remove('drag-over');

};

  

cell.ondrop = this.handleDrop.bind(this);

});

},

  

saveScheduleState() {

const scheduleState = {};

this.cells.forEach((cell, index) => {

const courseElement = cell.querySelector('.course-item');

if (courseElement) {

scheduleState[index] = courseElement.getAttribute('data-course-id');

}

});

localStorage.setItem('scheduleState', JSON.stringify(scheduleState));

},

  

loadScheduleState() {

const savedState = localStorage.getItem('scheduleState');

if (savedState) {

const scheduleState = JSON.parse(savedState);

Object.entries(scheduleState).forEach(([cellIndex, courseId]) => {

const cell = this.cells[cellIndex];

const courseElement = document.querySelector(`[data-course-id="${courseId}"]`);

if (cell && courseElement) {

const newCourse = courseElement.cloneNode(true);

newCourse.setAttribute('draggable', true);

newCourse.ondragstart = (e) => {

e.target.classList.add('dragging');

e.dataTransfer.setData('text/plain', e.target.getAttribute('data-course-id'));

};

newCourse.ondragend = (e) => {

e.target.classList.remove('dragging');

};

cell.appendChild(newCourse);

}

});

}

},

  

handleDrop(e) {

e.preventDefault();

const cell = e.currentTarget;

cell.classList.remove('drag-over');

const courseId = e.dataTransfer.getData('text/plain');

const courseElement = document.querySelector(`[data-course-id="${courseId}"]`);

// 如果课程已经在其他单元格中,创建一个副本

const newCourse = courseElement.cloneNode(true);

newCourse.setAttribute('draggable', true);

newCourse.ondragstart = (e) => {

e.target.classList.add('dragging');

e.dataTransfer.setData('text/plain', e.target.getAttribute('data-course-id'));

};

newCourse.ondragend = (e) => {

e.target.classList.remove('dragging');

};

// 检查单元格是否已有课程

if (cell.querySelector('.course-item')) {

const existingCourse = cell.querySelector('.course-item');

cell.removeChild(existingCourse);

}

cell.appendChild(newCourse);

// 保存课程表状态

this.saveScheduleState();

}

};

  

// 添加更多样式

const style = document.createElement('style');

style.textContent = `

.schedule-cell {

min-height: 80px;

border: 1px solid #ddd;

padding: 8px;

background-color: #fff;

}

.course-item {

padding: 8px;

margin: 4px;

border-radius: 4px;

cursor: move;

color: #fff;

text-shadow: 1px 1px 1px rgba(0,0,0,0.2);

box-shadow: 2px 2px 4px rgba(0,0,0,0.1);

}

.dragging {

opacity: 0.5;

}

.drag-over {

background-color: #e9ecef;

}

.time-column {

white-space: pre-line;

font-size: 12px;

}

`;

document.head.appendChild(style);

  

// 初始化拖拽功能

document.addEventListener('DOMContentLoaded', () => {

DragSchedule.init();

});

拖拽排序

<!DOCTYPE html>

<html lang="zh">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>拖拽排序示例</title>

<style>

.sortable-list {

width: 300px;

margin: 20px auto;

padding: 0;

}

  

.sortable-item {

list-style: none;

background-color: #f0f0f0;

margin: 5px 0;

padding: 10px 15px;

border-radius: 4px;

cursor: move;

transition: background-color 0.3s;

}

  

.sortable-item.dragging {

opacity: 0.5;

background-color: #e0e0e0;

}

  

.sortable-item:hover {

background-color: #e8e8e8;

}

</style>

</head>

<body>

<ul class="sortable-list">

<li class="sortable-item" draggable="true">项目 1</li>

<li class="sortable-item" draggable="true">项目 2</li>

<li class="sortable-item" draggable="true">项目 3</li>

<li class="sortable-item" draggable="true">项目 4</li>

<li class="sortable-item" draggable="true">项目 5</li>

</ul>

  

<script>

const sortableList = document.querySelector('.sortable-list');

let draggingItem = null;

  

// 为每个列表项添加拖拽事件监听器

document.querySelectorAll('.sortable-item').forEach(item => {

item.addEventListener('dragstart', handleDragStart);

item.addEventListener('dragend', handleDragEnd);

item.addEventListener('dragover', handleDragOver);

item.addEventListener('drop', handleDrop);

});

  

function handleDragStart(e) {

draggingItem = this;

this.classList.add('dragging');

// 设置拖拽效果

e.dataTransfer.effectAllowed = 'move';

e.dataTransfer.setData('text/plain', ''); // 必须调用setData才能在Firefox中触发drop

}

  

function handleDragEnd(e) {

this.classList.remove('dragging');

draggingItem = null;

}

  

function handleDragOver(e) {

e.preventDefault();

if (this === draggingItem) return;

  

// 获取鼠标位置相对于当前项的位置

const rect = this.getBoundingClientRect();

const midY = rect.top + rect.height / 2;

if (e.clientY < midY) {

// 如果鼠标在元素上半部分,就插入到当前元素之前

sortableList.insertBefore(draggingItem, this);

} else {

// 如果鼠标在元素下半部分,就插入到当前元素之后

sortableList.insertBefore(draggingItem, this.nextSibling);

}

}

  

function handleDrop(e) {

e.preventDefault();

}

</script>

</body>

</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2245207.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Throwable、IO流、Java虚拟机

Error和Exception stream结尾都是字节流&#xff0c;reader和writer结尾都是字符流 两者的区别就是读写的时候一个是按字节读写&#xff0c;一个是按字符。 实际使用通常差不多。 在读写文件需要对内容按行处理&#xff0c;比如比较特定字符&#xff0c;处理某一行数据的时候一…

lanqiao OJ 364 跳石头

这个题目的条件是移动的石头数量给定&#xff0c;但是最小移动距离的最大值我们不知道&#xff0c;所以要通过mid来“猜测”。如果当前的mid需要移动的最小石头数量超过给定数&#xff0c;则mid不成立&#xff0c;需要缩小&#xff0c;反之则增大mid&#xff0c;直至找到一个最…

「一」HarmonyOS端云一体化概要

关于作者 白晓明 宁夏图尔科技有限公司董事长兼CEO、坚果派联合创始人 华为HDE、润和软件HiHope社区专家、鸿蒙KOL、仓颉KOL 华为开发者学堂/51CTO学堂/CSDN学堂认证讲师 开放原子开源基金会2023开源贡献之星 「目录」 「一」HarmonyOS端云一体化概要 「二」体验HarmonyOS端云一…

Shell基础(7)

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团…

音视频pts/dts

现在的视频流有两个非常重要的时间戳&#xff0c;pts和dts&#xff0c;其中pts是显示的时候用&#xff0c;dts在解码的时候用。 pts很好理解&#xff0c;按照pts的顺序以及duration不间断的display就可以了。 dts在解码的时候用&#xff0c;那么这句话怎么理解&#xff0c;解…

sql server怎样用sql profiler捕获带变量值的慢sql

一 新建跟踪 点击工具-SQL Server Profiler&#xff1a; 点击文件-新建跟踪的按钮&#xff1a; 在‘事件选择’选项卡只选择如下两项内容&#xff08;RPC:Completed,SQL:BatchCompleted&#xff09;&#xff0c;把多余的取消勾选&#xff1a; 然后勾选上面截图中右下方的‘显示…

二叉树——输出叶子到根节点的路径

目录 代码 算法思想 例子 思维拓展 代码 int LeaveBit(Bitree T,int flag,int g) {if (!T) {return 0;}if (T->rchild NULL && T->lchild NULL) {//cout << "empty:" << T->data << endl;s.push(T->data);while (!s.emp…

深入理解Spring(三)

目录 2.1.3、Spring配置非自定义Bean 1)配置Druid数据源交由Spring管理 2)配置Connection交由Spring管理 3)配置日期对象交由Spring管理 4)配置MyBatis的SqlSessionFactory交由Spring管理 2.1.4、Bean实例化的基本流程 1)Bean信息定义对象-BeanDefinition 2)DefaultLi…

React Native 基础

React 的核心概念 定义函数式组件 import组件 要定义一个Cat组件,第一步要使用 import 语句来引入React以及React Native的 Text 组件: import React from react; import { Text } from react-native; 定义函数作为组件 const CatApp = () => {}; 渲染Text组件

SpringBoot,IOC,DI,分层解耦,统一响应

目录 详细参考day05 web请求 1、BS架构流程 2、RequestParam注解 完成参数名和形参的映射 3、controller接收json对象&#xff0c;使用RequestBody注解 4、PathVariable注解传递路径参数 5、ResponseBody&#xff08;return 响应数据&#xff09; RestController源码 6、统一响…

Linux:confluence8.5.9的部署(下载+安装+pojie)离线部署全流程 遇到的问题

原文地址Linux&#xff1a;confluence8.5.9的部署&#xff08;下载安装破ji&#xff09;离线部署全流程_atlassian-agent-v1.3.1.zip-CSDN博客 背景&#xff1a;个人使用2核4g 内存扛不住 总是卡住&#xff0c;但是流程通了所以 直接公司开服务器干生产 个人是centos7 公司…

线程池的实现与应用

一、线程池 一种线程使用模式。线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性能。而线程池维护着多个线程&#xff0c;等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用&#xff0c…

.net 8使用hangfire实现库存同步任务

C# 使用HangFire 第一章:.net Framework 4.6 WebAPI 使用Hangfire 第二章:net 8使用hangfire实现库存同步任务 文章目录 C# 使用HangFire前言项目源码一、项目架构二、项目服务介绍HangFire服务结构解析HangfireCollectionExtensions 类ModelHangfireSettingsHttpAuthInfoUs…

EventListener与EventBus

EventListener JDK JDK1.1开始就提供EventListener&#xff0c;一个标记接口&#xff0c;源码如下&#xff1a; /*** A tagging interface that all event listener interfaces must extend.*/ public interface EventListener { }JDK提供的java.util.EventObject&#xff1…

优先级队列PriorityQueue(堆)

1. 优先级队列 队列是一种先进先出的数据结构,而如果我们操作的数据带有优先级,我们出队的时候就会先出队优先级最高的元素.比如打游戏的时候有人给你打电话,操作系统看来有俩个进程,优先会处理打电话. 主要功能 1> 返回最高优先级对象 2> 添加新的对象 2. 堆的概念 2.1 …

【AI】人工智能报告解读——中国人工智能的发展

自 2016 年 AlphaGo 与世界顶级围棋选手对战后&#xff0c;AI 概念和技术从此走入大众视野。2017 年&#xff0c;国务院颁布《新一代人工智能发展规划》&#xff0c;这是中国在人工智能领域第一个部署文件&#xff0c;确定了人工智能产业发展的总体思路、战略目标和任务。技术和…

Flutter:photo_view图片预览功能

导入SDK photo_view: ^0.15.0单张图片预览&#xff0c;支持放大缩小 import package:flutter/material.dart; import package:photo_view/photo_view.dart;... ...class _MyHomePageState extends State<MyHomePage>{overrideWidget build(BuildContext context) {return…

uni-app 修改复选框checkbox选中后背景和字体颜色

编写css&#xff08;注意&#xff1a;这个样式必须写在App.vue里&#xff09; /* 复选框 */ /* 复选框-圆角 */ checkbox.checkbox-round .wx-checkbox-input, checkbox.checkbox-round .uni-checkbox-input {border-radius: 100rpx; } /* 复选框-背景颜色 */ checkbox.checkb…

c++中mystring运算符重载

#include <iostream> #include <cstring>using namespace std;class mystring {char* buf; public:mystring(); //构造函数mystring(const char * str); //构造函数mystring(const mystring& str); //深拷贝函数void show(); //输出函数void setmystr(const my…

oracle数据恢复—通过拼接数据库碎片的方式恢复Oracle数据的案例

Oracle数据库故障&#xff1a; 存储掉盘超过上限&#xff0c;lun无法识别。管理员重组存储的位图信息并导出lun&#xff0c;发现linux操作系统上部署的oracle数据库中有上百个数据文件的大小变为0kb。数据库的大小缩水了80%以上。 取出&并分析oracle数据库的控制文件。重组…