1501

8 分钟

#课程表

本页面是一个使用 HTML + CSS + 原生 JavaScript 实现的简易课程表制作工具,支持 直接编辑拖拽填充课程,并可一键打印,适合作为教学示例或实际使用的小工具。

课程表

#一、页面结构说明

页面整体分为三个主要区域:

  1. 左侧课程列表
  2. 右侧课程表
  3. 底部操作与提示区域

整体布局使用 flex 实现,左右分栏清晰,操作直观。

#二、课程列表区域

左侧显示一个课程列表,每一项课程都是一个可拖拽的卡片:

  • 课程数据由 JavaScript 中的数组 classList 定义
  • 每个课程项使用 div 元素渲染
  • 通过 draggable="true" 启用 HTML5 原生拖拽功能
  • 样式上使用浅绿色背景,便于区分和拖拽识别

用户可以将任意课程从此列表拖入右侧课程表的单元格中。

#三、课程表区域

右侧是一个标准的 HTML 表格,用于展示课程表结构。

#1. 表格结构

  • 表头为一周七天(星期一到星期日)
  • 行按 上午 / 午休 / 下午 分区
  • 使用 rowspancolspan 合并单元格,结构清晰
  • 每个可放置课程的单元格都带有 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>

更新: 2026/1/4

作者: PlanC

创建: 2026/1/4