#课程表
本页面是一个使用 HTML + CSS + 原生 JavaScript 实现的简易课程表制作工具,支持 直接编辑 与 拖拽填充课程,并可一键打印,适合作为教学示例或实际使用的小工具。
课程表
#一、页面结构说明
页面整体分为三个主要区域:
- 左侧课程列表
- 右侧课程表
- 底部操作与提示区域
整体布局使用 flex 实现,左右分栏清晰,操作直观。
#二、课程列表区域
左侧显示一个课程列表,每一项课程都是一个可拖拽的卡片:
- 课程数据由 JavaScript 中的数组
classList定义 - 每个课程项使用
div元素渲染 - 通过
draggable="true"启用 HTML5 原生拖拽功能 - 样式上使用浅绿色背景,便于区分和拖拽识别
用户可以将任意课程从此列表拖入右侧课程表的单元格中。
#三、课程表区域
右侧是一个标准的 HTML 表格,用于展示课程表结构。
#1. 表格结构
- 表头为一周七天(星期一到星期日)
- 行按 上午 / 午休 / 下午 分区
- 使用
rowspan、colspan合并单元格,结构清晰 - 每个可放置课程的单元格都带有
data-slot="true"标记
#2. 可编辑设计
-
表格标题(caption)、表头、时间段和单元格内容均使用
contenteditable="true" -
用户可以:
- 修改课程表标题
- 修改星期名称
- 直接点击单元格手动输入课程
无需额外表单即可完成编辑。
#四、拖拽交互说明
页面通过 HTML5 Drag & Drop API 实现课程拖拽功能,交互逻辑集中绑定在 #main 容器上。
#1. 拖拽开始
-
当用户开始拖拽课程卡片时:
- 记录拖拽源元素
- 设置拖拽效果为
copy
#2. 拖拽经过与提示
-
在可放置的单元格上方拖动时:
- 阻止默认行为,允许放置
- 给单元格添加高亮样式,提示可放置区域
#3. 放置课程
-
松开鼠标后:
- 将课程名称写入目标单元格
- 移除高亮样式
-
不会影响左侧课程列表,实现“复制式”拖拽
#五、打印支持
页面针对打印场景进行了专门优化:
-
使用
@media print隐藏:- 页面标题
- 左侧课程列表
- 操作按钮和提示文字
-
打印时仅保留干净、规范的课程表内容
-
点击“打印”按钮即可直接调用浏览器打印功能
#六、适用场景
该页面适用于:
- HTML / JavaScript 教学示例
- 学生或教师制作课程表
- 展示 HTML5 拖拽与
contenteditable的实际用法 - 作为无需后端的小型工具页面
#七、特点总结
- ✅ 无依赖、纯原生实现
- ✅ 拖拽 + 直接编辑,操作简单
- ✅ 结构清晰,易于扩展
- ✅ 支持打印,实用性强
#源码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>课程表制作</title>
<link rel="canonical" href="https://xplanc.org/primers/document/zh/03.HTML/90.实践案例/01.课程表.md"/>
<style>
#root {
display: flex;
flex-direction: column;
gap: 8px;
align-items: center;
}
#main {
display: flex;
flex-direction: row;
gap: 8px;
}
#classList {
padding: 4px;
border: 2px solid deepskyblue;
display: flex;
flex-direction: column;
gap: 8px;
}
#title {
font-weight: bolder;
margin-bottom: 8px;
padding: 4px;
border: 2px solid deepskyblue;
}
.classCard {
padding: 0.25rem 1rem;
background: palegreen;
}
table {
border-collapse: collapse;
}
th, td {
width: 80px;
height: 45px;
border: 2px solid deepskyblue;
text-align: center;
}
#tips {
display: flex;
flex-direction: row;
gap: 8px;
align-items: center;
}
.drag-over {
background: rgba(0,0,0,0.1);
}
@media print {
.no-print, .no-print * {
display: none !important;
}
}
</style>
</head>
<body>
<div id="root">
<h1 class="no-print">课程表制作</h1>
<main id="main">
<div id="classList" class="no-print"></div>
<div id="schedule">
<table>
<caption id="title" contenteditable="true">课程表</caption>
<thead>
<tr>
<th contenteditable="true"></th>
<th contenteditable="true">星期一</th>
<th contenteditable="true">星期二</th>
<th contenteditable="true">星期三</th>
<th contenteditable="true">星期四</th>
<th contenteditable="true">星期五</th>
<th contenteditable="true">星期六</th>
<th contenteditable="true">星期日</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="4" contenteditable="true"> 上午 </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
</tr>
<tr>
<!-- <td contenteditable="true" data-slot="true"> </td> -->
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
</tr>
<tr>
<!-- <td contenteditable="true" data-slot="true"> </td> -->
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
</tr>
<tr>
<!-- <td contenteditable="true" data-slot="true"> </td> -->
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
</tr>
<tr>
<td colspan="8" contenteditable="true">午休</td>
</tr>
<tr>
<td rowspan="4" contenteditable="true">下午</td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
</tr>
<tr>
<!-- <td contenteditable="true" data-slot="true"> </td> -->
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
</tr>
<tr>
<!-- <td contenteditable="true" data-slot="true"> </td> -->
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
</tr>
<tr>
<!-- <td contenteditable="true" data-slot="true"> </td> -->
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
<td contenteditable="true" data-slot="true"> </td>
</tr>
</tbody>
</table>
</div>
</main>
<div id="tips" class="no-print">
<p>将左侧课程拖入单元格,也可以点击单元格直接编辑。</p>
<button onclick="print()">打印</button>
</div>
</div>
<script>
const classList = ["语文", "数学", "英语", "物理", "化学", "生物", "政治", "历史", "地理", "美术", "音乐", "体育"]
const classListDiv = document.querySelector('#classList');
classListDiv.innerHTML = classList.map((item)=>`<div class="classCard" draggable="true">${item}</div>`).join('\n');
const mainDiv = document.querySelector("#main");
let source = undefined;
mainDiv.ondragstart = (ev) => {
ev.dataTransfer.effectAllowed = 'copy';
source = ev.target;
}
mainDiv.ondragover = (ev) => {
ev.preventDefault();
}
mainDiv.ondragenter = (ev) => {
if (ev.target.dataset.slot === "true") {
ev.target.classList.add('drag-over')
}
}
mainDiv.ondragleave = (ev) => {
ev.target.classList.remove('drag-over');
}
mainDiv.ondrop = (ev) => {
ev.target.classList.remove('drag-over');
if (ev.target.dataset.slot === "true") {
ev.target.innerText = source.innerText;
}
}
</script>
</body>
</html>