iT邦幫忙

2021 iThome 鐵人賽

DAY 27
0
Modern Web

舌尖上的JS系列 第 27

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


上一篇
D26 - 走!去瀏覽器重現奧運決勝點 in
下一篇
D28 - 走!去瀏覽器玩轉黑膠唱片 Web Audio API
系列文
舌尖上的JS30
0
Chiahsuan
iT邦新手 5 級 ‧ 2021-10-12 20:35:13

超有創意~~~/images/emoticon/emoticon24.gif

0
wendy
iT邦新手 5 級 ‧ 2021-10-12 20:45:04

讚!!!!

0
Rex
iT邦新手 5 級 ‧ 2021-10-12 20:49:55

哇啊~~~~
/images/emoticon/emoticon71.gif

0
南國ㄟ安迪
iT邦新手 5 級 ‧ 2021-10-12 20:53:49

遲來...! /images/emoticon/emoticon24.gif

我要留言

立即登入留言