【DOM】实现一个课程表的拖拽功能

历史小剧场

一个皇帝从不需要用个人的良好品格来证明自己的英明,恰恰相反,在历史上,干皇帝这行的人基本都不是什么好人,因为好人干不了皇帝,朱匀炆就是铁证。----《明朝那些事儿》

1. 前置知识

1.1 DataTransfer接口

介绍一个属性:

  • dataTransfer.effectAllowed: 提供所有可用的操作类型。必须是 none、copy、copyLink、copyMove、link、linkMove、move、all or uninitialized

通过这个接口,我们可以控制拖拽元素的显示和操作类型。
例如:当effectAllowed设置为move时,拖拽元素不会出现+号

1.2 四个事件

一个拖拽元素操作过程会分别触发

  • dragstart: 当用户开始拖动元素或选择文本时触发此事件;
  • dragover: 当将元素或文本选择拖动到有效放置目标(每几百毫秒)上时,会触发此事件;
  • dragenter: 当拖动的元素或选择文本输入有效的放置目标时,会触发此事件;
  • drop:拖动元素或选择文本时放开时触发此事件。注意:需要阻止浏览器默认行为。

2. 实现课程表案例

2.1 准备静态页面

左侧课程,右侧表格

<div class="container">
  <div class="left" data-drop="move">
      <div data-effect="copy" draggable="true" class="item color1">语文</div>
      <div data-effect="copy" draggable="true" class="item color2">数学</div>
      <div data-effect="copy" draggable="true" class="item color3">英语</div>
      <div data-effect="copy" draggable="true" class="item color4">历史</div>
      <div data-effect="copy" draggable="true" class="item color5">地理</div>
      <div data-effect="copy" draggable="true" class="item color6">政治</div>
      <div data-effect="copy" draggable="true" class="item color7">体育</div>
  </div>
  <div class="right">
      <table border>
          <caption>
              <h1>课程表</h1>
          </caption>
          <colgroup>
              <col />
              <col />
              <col />
              <col />
              <col />
              <col />
              <col />
              <col />
          </colgroup>
          <thead>
              <tr>
                  <td></td>
                  <th>星期一</th>
                  <th>星期二</th>
                  <th>星期三</th>
                  <th>星期四</th>
                  <th>星期五</th>
                  <th>星期六</th>
                  <th>星期天</th>
              </tr>
          </thead>
          <tbody>
              <tr>
                  <th rowspan="4" class="span">上午</th>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
              </tr>
              <tr>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
              </tr>
              <tr>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
              </tr>
              <tr>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
              </tr>

              <tr>
                  <td colspan="8"></td>

              </tr>

              <tr>
                  <th rowspan="4" class="span">下午</th>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
              </tr>
              <tr>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
              </tr>
              <tr>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
                  <td data-drop="copy"></td>
              </tr>
          </tbody>
      </table>
  </div>
</div>
2.2 添加样式
* {
   margin: 0;
    padding: 0;
}

.container {
    margin: 0;
    display: flex;
    align-items: center;
    width: 100vw;
    height: 100vh;
}

.left {
    display: flex;
    justify-content: space-around;
    align-items: center;
    flex-direction: column;
    width: 100px;
    height: 400px;
    flex-shrink: 0;
    background-color: whitesmoke;
}

.item {
    width: 80px;
    height: 50px;
    text-align: center;
    line-height: 50px;
    color: white;
}

.color1 {
    background-color: skyblue;
}

.color2 {
    background-color: red;
}

.color3 {
    background-color: orange;
}

.color4 {
    background-color: deeppink;
}

.color5 {
    background-color: green;
}

.color6 {
    background-color: blue;
}
.color7 {
    background-color: salmon;
}

.right {
    background-color: whitesmoke;
    margin: 0 20px 0 12px;
}

.right table td {
    width: 80px;
    height: 50px;
    transition: all 1s ease-in-out;
}

.drop-over {
    background-color: #999;
}
2.3 添加拖拽事件逻辑
const container = document.querySelector(".container")

let source;
container.ondragstart = (e) => {
    console.log("start => ", e.target)
    // 设置鼠标状态为移动
    e.dataTransfer.effectAllowed = e.target.dataset.effect
    source = e.target;
}

// 会一直触发
container.ondragover = (e) => {
    // console.log("over => ", e.target)
    // 阻止浏览器默认行为
    e.preventDefault()
}

const clearDropStyle = () => {
    document.querySelectorAll('.drop-over').forEach(node => {
        node.classList.remove('drop-over')
    })
}

const getDropNode = (node) => {
    while (node) {
        if (node.dataset.drop) {
            return node;
        }
        node = node.parentNode;
    }
}

// 只会触发一次
container.ondragenter = (e) => {
    console.log("enter => ", e.target)
    // 清楚之前的drop-over
    clearDropStyle()
    const dropNode = getDropNode(e.target)
    if (dropNode && Object.is(dropNode.dataset.drop, e.dataTransfer.effectAllowed)) {
        dropNode.classList.add('drop-over')
    }
}

container.ondrop = (e) => {
    console.log("drop => ", e.target)
    clearDropStyle()
    const dropNode = getDropNode(e.target)
    if (dropNode && Object.is(dropNode.dataset.drop, e.dataTransfer.effectAllowed)) {
        if (dropNode.dataset.drop === 'copy') {
            dropNode.innerHTML = ''
            const cloned = source.cloneNode(true);
            cloned.dataset.effect = 'move'
            dropNode.appendChild(cloned)
        } else {
            // 清除
            source.remove()
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值