D27 - 走!去浏览器学 Drag & Drop 自己组汉堡包

前言

来学拖拉事件自己组汉堡包~~

实作连结

拖拉事件 drag & drop

拖拉事件 Drag Event 指的是使用者按住滑鼠不放,移动物体到另一个位置,再放开滑鼠将物体放置他处。
按住滑鼠的事件称为 drag,放开滑鼠的事件称为 drop

拖拉事件对象

  • 对象:html 的图片、连结预设可以拖拉,其他的标签须加上属性 draggable 设定才可以拖拉
<div draggable='true'></div>
  • 若使用拖拉属性,滑鼠无法再取得内部的文字或是子节点
  • 网页大部分区域不适合作为 drop 的目标节点,所以须组指默认事件
<div ondragover="event.preventDefault()">

拖拉事件种类

  1. drag:拖拉过程会持续触发
  2. dragstart:拖拉时在被拖拉的节点上触发,事件的 target 指向被拖拉的节点
  3. dragend:拖拉结束时(松开滑鼠或按下 escape 键),在被拖拉的节点上触发,事件的 target 是被拖拉的节点,与 dragstart 在同一个节点上触发。
  4. dragenter:拖拉进当前节点时,在当前节点上触发,事件的 target 指向当前节点。
  5. dragover:拖拉到当前节点时,在当前节点持续触发,事件的 target 为当前节点。
  6. dragleave:拖拉离开当前节点范围时,在当前节点上触发,事件 target 为当前节点
  7. drop:被拖拉的节点释放到目标节点时,在目标节点上触发,但若是目标节点不允许 drop 行为,即时在目标节点上松开滑鼠,也不会触发 drop 事件。

开启 drop 设定

由於大部分的网页区域预设不允许 drop,若要使用 drop 须取消预设行为,使用语法 event.preventDefault()

设定方式是使用 dragenterdragover,当侦测到物体拖拉到范围时,在监听函数内加上 event.preventDefault() 就可以允许物体 drop 到目标范围。

拖拉复制: drop 搭配 clone

拖拉再 drop 有两种情况:

  1. 目标物体完全移动到新位置
  2. 目标物体复制一个新的到新位置,原位置的物体保持不动

先来看看第二种情况,要将目标物从 a 点复制到 b 点,从 createElement 改用 Node.cloneNode() 更方便!可以完全复制目标物的 css 设定,再用 appendChild 放置到 b 的节点下就完成拷贝行为罗~

node.cloneNode(deep); // deep 参数若为 true 表示连同子节点一起复制,false 则复制自己本身

做出来的画面像这样:
codepen drop 玩玩看

大侠自己组汉堡包

那麽如果是情况一:移动目标位置呢?
发挥想像力看看,是不是很适合来个大侠自组汉堡!
汉堡包实作连结

设计想法:使用者可以依照想要的食材,自己组装汉堡。

<body>
  <h1>BURGER</h1>
  <div class="ingredient">
    <img src='url' alt='汉堡上盖' class="target" draggable="true">
    <img src='url' alt='肉' class="target" draggable="true">
    <img src='url' alt='肉' class="target" draggable="true">
    <img src='url' alt='番茄' class="target" draggable="true">
    <img src='url' alt='汉堡下盖' class="target" draggable="true">
    <img src='url' alt='青菜' class="target vagie" draggable="true">
    <img src='url' alt='起司' class="target cheese" draggable="true">
  </div>
  <div class="assembly"></div>
</body>

JavaScript 实作:

在 document 下设定 dragstart 监听事件,每次抓取的 event.target 会指向滑鼠拖拉的元素,将元素移动到桌布范围时,使用 drop 将元素添加到桌布的节点下!

// 取节点
let get = (tag) => document.querySelector(tag);
let draggedTarget = null; // 宣告被抓取的目标物变数

// 设定抓取时的监听事件
document.addEventListener("dragstart", function (event) {
  console.log("抓到目标"); // test code
  draggedTarget = event.target; // 将抓取时的目标物存入变数中
});

// 设定汉堡放置区的监听事件
get(".assembly").addEventListener("drop", function (event) {
  let previousIngredient = this.children[0]; // 取目前汉堡最上层材料
 // 用 insertBefore ,将新抓取的材料叠在目前最上层的材料前,才可以制造汉堡往上堆叠的效果,否则用 appendChild 是变成往下叠唷!
  this.insertBefore(draggedTarget, previousIngredient); 
});

// 取消预设行为,才能使用 drop
document.addEventListener("dragover", function (event) {
  event.preventDefault();
});

// 当没有材料时拔掉材料区
document.body.addEventListener("drop", function (event) {
  if (get(".ingredient").childElementCount === 0) {
    let emptyBox = get(".ingredient");
    this.removeChild(emptyBox);
  }
});

Reference

MDN - clone
JavaScript Info
W3Cschool

结语

随着 Web API 知识增加可以玩得操作越来越多了~~~ 但碰到 css 都不小心太入迷,汉堡实作的 css 美化时间应该是写逻辑的 2 倍以上 ...

/images/emoticon/emoticon25.gif


<<:  [Day 27]从零开始学习 JS 的连续-30 Days---BOM-浏览器物件模型(上)

>>:  [D27] 物件侦测(8)

自动化 End-End 测试 Nightwatch.js 串接 gitlab CI/CD

既然可以在 local 执行 E2E 了,与其占用一个 terminal 并让电脑跑,不如就交给 g...

Day 11 ( 中级 ) 拍手换图案 ( 二代板 )

拍手换图案 ( 二代板 ) 教学原文参考:拍手换图案 ( V2 ) 这篇文章是针对 micro:bi...

Day 17 : Add Two Numbers

这一题题目会给我们两个Linked Lists,分别代表两个非负整数。题目要我们把两个数相加後回传一...

【Day 15】Function - Practice 1

前言 其实分享我写题目的过程也算是一种自我检讨,所以使用的方法可能不会是最完美的,还请各读者多多包涵...

[早餐吃到饱-3] 温莎咖啡厅 - 裕元花园酒店 Breakfast Buffet at Windsor Hotel in Taichung

好的,昨天的星飨道是5点起床,温莎就更拚了,4点半XD 今天还是用早餐跟大家道声早安呦~~ 由於台中...