HTML5拖拽API学习 托拽排序和可托拽课程表
文章目录
- 前言
- 拖拽API核心概念
- 拖拽式使用流程
- 例子
- 注意事项
- 综合例子🌰 可拖拽课程表
- 拖拽排序
前言
前端拖拽功能让网页元素可以通过鼠标或触摸操作移动。HTML5 提供了标准的拖拽API,简化了拖放操作的实现。以下是拖拽API的基本使用指南:
拖拽API核心概念
- draggable属性:设置元素的
draggable="true"
属性,允许用户拖动该元素。
<div draggable="true">可拖动元素</div>
- dragstart事件:拖动开始时触发,可以设置拖动数据。
const draggableElement = document.querySelector('div');
draggableElement.addEventListener('dragstart', (event) => {
event.dataTransfer.setData('text/plain', '拖动数据');
});
- dragover事件:拖动元素在目标区域上方时触发,需要调用
event.preventDefault()
以允许放置。
const dropZone = document.querySelector('#dropZone');
dropZone.addEventListener('dragover', (event) => {
event.preventDefault(); // 允许放置
});
- drop事件:拖动元素放置到目标区域时触发,可以获取拖动数据。
dropZone.addEventListener('drop', (event) => {
event.preventDefault();
const data = event.dataTransfer.getData('text/plain');
console.log('放置的数据:', data);
});
- dragend事件:拖动操作结束时触发,用于清理拖动状态或重置样式。
draggableElement.addEventListener('dragend', () => {
draggableElement.style.backgroundColor = ''; // 重置样式
});
拖拽式使用流程
- 设置可拖拽元素:在HTML中为元素添加
draggable="true"
属性。 - 处理拖拽开始事件:在
dragstart
事件中设置拖拽数据。 - 设置目标区域:通过
dragover
事件处理,允许放置操作。 - 处理放置事件:在
drop
事件中获取数据并处理放置逻辑。 - 清理拖拽操作:在
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>