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

文章目录

前言

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

拖拽API核心概念

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

拖拽式使用流程

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

例子

html 复制代码
<!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获取文件数据。

综合例子🌰 可拖拽课程表

html 复制代码
<!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>
js 复制代码
//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();

});

拖拽排序

html 复制代码
<!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>
相关推荐
A懿轩A2 分钟前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
一个处女座的程序猿O(∩_∩)O1 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink4 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者6 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-6 小时前
验证码机制
前端·后端
燃先生._.7 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
南宫生8 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
高山我梦口香糖8 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
sanguine__8 小时前
Web APIs学习 (操作DOM BOM)
学习