Language/JavaScript (WEB)

๐ŸŒ ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ(Drag and Drop) ๊ธฐ๋Šฅ ์ดํ•ด & ๊ตฌํ˜„ํ•˜๊ธฐ

์ธํŒŒ_ 2022. 7. 23. 14:23

javascript-drag-drop

HTML ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ ์‚ฌ์šฉ๋ฒ•

๋“œ๋ž˜๊ทธ(drag)์™€ ๋“œ๋กญ(drop)์€ ์ปดํ“จํ„ฐ๋ฅผ ์ด์šฉํ•˜๋ฉด์„œ ์ •๋ง ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋Šฅ ์ค‘์— ํ•˜๋‚˜์ผ ๊ฒƒ์ด๋‹ค. ํŒŒ์ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฌธ์„œ๋ฅผ ๋ณต์‚ฌํ•ด ์ด๋™ํ•˜๋Š” ๊ฒƒ ๋ถ€ํ„ฐ ์ฃผ๋ฌธ ํ•˜๋ ค๋Š” ๋ฌผ๊ฑด์„ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ๋“œ๋กญํ•˜๋Š” ๊ฒƒ ๊นŒ์ง€ ์ผ์ƒ์ƒํ™œ์—์„œ ๋งŽ์ด ์ ‘ํ•ด ๋ดค์„ ๊ฒƒ์ด๋‹ค. 

javascript-drag-drop

HTML ๊ทธ๋ฆฌ๊ณ  JavaScript์—์„œ์˜ ๋“œ๋ž˜๊ทธ ๋“œ๋กญ์€ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘๋™ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ๋งˆ์šฐ์Šค ์ปค์„œ๋กœ ๊ฐ์ฒด(object)๋ฅผ ๋“œ๋ž˜๊ทธํ•ด์„œ ๋†“์„ ๋•Œ๊นŒ์ง€ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„์˜ ์ด๋ฒคํŠธ๊ฐ€ ์ˆœ์ฐจ์ ์œผ๋กœ ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜์–ด ๋™์ž‘์ด ์™„๋ฃŒ๋˜๊ฒŒ ๋œ๋‹ค.

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ด์šฉํ•ด ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ ์ด๋ฒคํŠธ ๋™์ž‘ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๊ณ  ์—ฌ๋Ÿฌ ์˜ˆ์ œ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์ ธ๋ณผ ์˜ˆ์ •์ด๋‹ค.


๋“œ๋ž˜๊ทธ & ๋“œ๋กญ ์ด๋ฒคํŠธ ์ข…๋ฅ˜

HTML์—์„œ ์š”์†Œ๊ฐ€ ๋“œ๋ž˜๊ทธ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋‹น ์š”์†Œ์˜ ์†์„ฑ์œผ๋กœ draggable="true" ๊ฐ’์„ ์ฃผ๋ฉด ์š”์†Œ์™€ ์ƒํ˜ธ๋™์ž‘ ํ• ๋•Œ๋งˆ๋‹ค ์•„๋ž˜์™€ ๊ฐ™์€ ๋“œ๋ž˜๊ทธ ๋“œ๋กญ ์ด๋ฒคํŠธ๋“ค์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.

<div draggable="true" class="item">
  <p>๋“œ๋ž˜๊ทธ์šฉ ์•„์ดํ…œ</p>
</div>
ํƒœ๊ทธ ์ค‘์— ๋””ํดํŠธ๋กœ draggableํ•œ ์š”์†Œ๊ฐ€ ๋ช‡๊ฐ€์ง€ ์žˆ๋Š”๋ฐ, ์˜ˆ๋ฅผ ๋“ค์–ด <a>๋Š” ๊ฐ€๋ณธ์ ์œผ๋กœ ๋“œ๋ž˜๊ทธ ๊ฐ€๋Šฅํ•˜๊ณ , <span>์€ ๋“œ๋ž˜๊ทธ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. (์ง์ ‘ ๋งํฌ๋ฅผ ๋“œ๋ž˜๊ทธ ํ•ด๋ณด์ž)

 

๋‹ค์Œ์€ ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ์‹œ ์ผ์–ด๋‚˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ๋‚˜์—ดํ•œ ํ‘œ์ด๋‹ค. ๋ฌผ๋ก  ๋ฐ˜๋“œ์‹œ ์ด ์ด๋ฒคํŠธ๋“ค์„ ๋ชจ๋‘ ๋”ฐ๋ฅผ ํ•„์š”๋Š” ์—†๊ณ  ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ๊ฐ€์ ธ๊ฐ€์„œ ๊ตฌํ˜„ํ•˜๋ฉด ๋œ๋‹ค.

๋‹จ, ์ด์ค‘ drop, dragover ์ด๋ฒคํŠธ๋Š” ํ•„์ˆ˜๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ์ด๋ฒคํŠธ ์ด๋‹ค. dragover ์ด๋ฒคํŠธ๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด drop ์ด๋ฒคํŠธ๊ฐ€ ์ž‘๋™ํ•˜์ง€ ์•Š์œผ๋‹ˆ๊นŒ ๋ง์ด๋‹ค.

์ด๋ฒคํŠธ ์ˆœ์„œ ์„ค๋ช…
dragstart 1. ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ์ฒด(object)๋ฅผ ๋“œ๋ž˜๊ทธํ•˜๋ ค๊ณ  ์‹œ์ž‘ํ•  ๋•Œ ๋ฐœ์ƒํ•จ.
drag 2. ๋Œ€์ƒ ๊ฐ์ฒด๋ฅผ ๋“œ๋ž˜๊ทธํ•˜๋ฉด์„œ ๋งˆ์šฐ์Šค๋ฅผ ์›€์ง์ผ ๋•Œ ๋ฐœ์ƒํ•จ.
dragenter 3. ๋งˆ์šฐ์Šค๊ฐ€ ๋Œ€์ƒ ๊ฐ์ฒด์˜ ์œ„๋กœ ์ฒ˜์Œ ์ง„์ž…ํ•  ๋•Œ ๋ฐœ์ƒํ•จ.
dragover 4. ๋“œ๋ž˜๊ทธํ•˜๋ฉด์„œ ๋งˆ์šฐ์Šค๊ฐ€ ๋Œ€์ƒ ๊ฐ์ฒด์˜ ์˜์—ญ ์œ„์— ์ž๋ฆฌ ์žก๊ณ  ์žˆ์„ ๋•Œ ๋ฐœ์ƒํ•จ.
drop 5. ๋“œ๋ž˜๊ทธ๊ฐ€ ๋๋‚˜์„œ ๋“œ๋ž˜๊ทธํ•˜๋˜ ๊ฐ์ฒด๋ฅผ ๋†“๋Š” ์žฅ์†Œ์— ์œ„์น˜ํ•œ ๊ฐ์ฒด์—์„œ ๋ฐœ์ƒํ•จ. ๋ฆฌ์Šค๋„ˆ๋Š” ๋“œ๋ž˜๊ทธ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์„œ ๋“œ๋กญ ์œ„์น˜์— ๋†“๋Š” ์—ญํ• ์„ ํ•จ
dragleave 6. ๋“œ๋ž˜๊ทธ๊ฐ€ ๋๋‚˜์„œ ๋งˆ์šฐ์Šค๊ฐ€ ๋Œ€์ƒ ๊ฐ์ฒด์˜ ์œ„์—์„œ ๋ฒ—์–ด๋‚  ๋•Œ ๋ฐœ์ƒํ•จ.
dragend 7. ๋Œ€์ƒ ๊ฐ์ฒด๋ฅผ ๋“œ๋ž˜๊ทธํ•˜๋‹ค๊ฐ€ ๋งˆ์šฐ์Šค ๋ฒ„ํŠผ์„ ๋†“๋Š” ์ˆœ๊ฐ„ ๋ฐœ์ƒํ•จ.
๊ธฐ๋ณธ์ ์œผ๋กœ HTML ์š”์†Œ๋Š” ๋‹ค๋ฅธ ์š”์†Œ์˜ ์œ„์— ์œ„์น˜ํ•  ์ˆ˜ ์—†๋‹ค. ๋”ฐ๋ผ์„œ ๋‹ค๋ฅธ ์š”์†Œ ์œ„์— ์œ„์น˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋†“์ผ ์žฅ์†Œ์— ์žˆ๋Š” ์š”์†Œ์˜ ๊ธฐ๋ณธ ๋™์ž‘์„ ๋ง‰์•„์•ผ๋งŒ ํ•œ๋‹ค. ์ด ์ž‘์—…์„ event.preventDefault() ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ๊ฐ„๋‹จํžˆ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

dragstart ์ด๋ฒคํŠธ

  • ๋“œ๋ž˜๊ทธ๊ฐ€ ์‹œ์ž‘๋˜๋Š” ์ˆœ๊ฐ„ ๋ฐœ์ƒ
const item = document.querySelector(".item");

item.addEventListener("dragstart", (e) => {
  console.log(e);
  console.log("๋“œ๋ž˜๊ทธ๋ฅผ ์‹œ์ž‘ํ•˜๋ฉด ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ");
});

See the Pen drag-drop-์—ฐ์Šต-dragstart by barzz12 (@inpaSkyrim) on CodePen.

 

drag ์ด๋ฒคํŠธ

  • ์š”์†Œ๋ฅผ ๋“œ๋ž˜๊ทธํ•  ๋•Œ ๋ฐœ์ƒ
const item = document.querySelector(".item");

item.addEventListener("drag", (e) => {
  console.log(e);
  console.log("๋“œ๋ž˜๊ทธํ•˜๋ฉด ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ");
});

See the Pen drag-drop-์—ฐ์Šต-1 by barzz12 (@inpaSkyrim) on CodePen.

 

dragenter ์ด๋ฒคํŠธ

  • ํ•ด๋‹น ์ด๋ฒคํŠธ๋ฅผ ์ง€์ •ํ•œ ์š”์†Œ์— ๋“œ๋ž˜๊ทธํ•œ ์•„์ดํ…œ์ด ๋“ค์–ด๊ฐ€๋ฉด ๋ฐœ์ƒ
  • ๋“œ๋ž˜๊ทธํ•˜๋Š” ์š”์†Œ๊ฐ€ dragenter ์ด๋ฒคํŠธ๋ฅผ ๋‹ฌ์•„๋†“์€ ์š”์†Œ ์•ˆ์— ์ง„์ž…ํ–ˆ์„ ๋•Œ ๋ฐœ์ƒ
// ๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ ์ด๋™ํ•˜์—ฌ ์œ„์น˜ํ•  ์šฐ์ธก ๋ฐ•์Šค ์˜์—ญ
const container2 = document.querySelector(".container2");

container2.addEventListener("dragenter", (e) => {
  console.log(e);
  console.log("๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ ์ด ์˜์—ญ์— ๋‹ฟ์œผ๋ฉด ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ");
});

See the Pen drag-drop-์—ฐ์Šต-3 by barzz12 (@inpaSkyrim) on CodePen.

 

dragover ์ด๋ฒคํŠธ

  • ์ด ์ด๋ฒคํŠธ๊ฐ€ ๋‹ฌ๋ฆฐ ์˜์—ญ ์œ„์— ๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ ์žˆ์œผ๋ฉด ๋ฐœ์ƒ
// ๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ ์ด๋™ํ•˜์—ฌ ์œ„์น˜ํ•  ์šฐ์ธก ๋ฐ•์Šค ์˜์—ญ
const container2 = document.querySelector(".container2");

container2.addEventListener("dragover", (e) => {
  console.log(e);
  console.log("๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ ์ด ์˜์—ญ์— ์œ„์— ๊ณ„์† ์œ„์น˜ํ•˜๋ฉด ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ");
});

See the Pen drag-drop-์—ฐ์Šต-dragover by barzz12 (@inpaSkyrim) on CodePen.

 

dragleave ์ด๋ฒคํŠธ

  • dragexit ์ด๋ฒคํŠธ ๋Œ€์‹  ์‚ฌ์šฉ.
  • ์–ด๋–ค ์š”์†Œ๋“  ๋“œ๋ž˜๊ทธ๋˜๊ณ  ์žˆ๋‹ค๋ฉด ์ด ์ด๋ฒคํŠธ๊ฐ€ ๋‹ฌ๋ฆฐ ์š”์†Œ์— ๋“ค์–ด๊ฐ”๋‹ค๊ฐ€ ๋‚˜๊ฐ€๋Š” ์‹œ์ ์— ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ
  • dragenter ์ด๋ฒคํŠธ์™€ ๋™์ž‘์ด ๊ฒน์น ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— e.preventDefault() ๋กœ ์ œํ•œํ•˜๋ฉฐ ๋‘˜์ด ๊ฒฐํ•ฉํ•˜์—ฌ ์‚ฌ์šฉํ•จ
// ์ฒ˜์Œ ๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ ์œ„์น˜ํ•˜๊ณ  ์žˆ๋Š” ์ขŒ์ธก ๋ฐ•์Šค ์˜์—ญ
const container = document.querySelector(".container");

container.addEventListener("dragenter", (e) => {
  e.preventDefault();
  console.log(e);
  console.log("๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ '์ฒซ' ๋ฒˆ์งธ ๋ฐ•์Šค ์˜์—ญ์— ์ตœ์ดˆ๋กœ ์ง„์ž…ํ–ˆ์„ ๋•Œ");
});
container.addEventListener("dragleave", (e) => {
  e.preventDefault();
  console.log(e);
  console.log("๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ '์ฒซ' ๋ฒˆ์งธ ๋ฐ•์Šค ์˜์—ญ์„ ๋– ๋‚˜๋ฉด ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ");
});


// ๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ ์ด๋™ํ•˜์—ฌ ์œ„์น˜ํ•  ์šฐ์ธก ๋ฐ•์Šค ์˜์—ญ
const container2 = document.querySelector(".container2");

container2.addEventListener("dragenter", (e) => {
  e.preventDefault();
  console.log(e);
  console.log("๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ '๋‘' ๋ฒˆ์งธ ๋ฐ•์Šค ์˜์—ญ์— ์ตœ์ดˆ๋กœ ์ง„์ž…ํ–ˆ์„ ๋•Œ");
});
container2.addEventListener("dragleave", (e) => {
  e.preventDefault();
  console.log(e);
  console.log("๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ '๋‘' ๋ฒˆ์งธ ๋ฐ•์Šค ์˜์—ญ์„ ๋– ๋‚˜๋ฉด ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ");
});

See the Pen drag-drop-์—ฐ์Šต-dragleave by barzz12 (@inpaSkyrim) on CodePen.

 

drop ์ด๋ฒคํŠธ

  • ์ด ์ด๋ฒคํŠธ๊ฐ€ ๋‹ฌ๋ฆฐ ์š”์†Œ์— ๋“œ๋ž˜๊ทธ๋ฅผ ๋๋‚ด๋ฉด ๋ฐœ์ƒ (dragover๋ž‘ ๊ฐ™์ด ์จ์•ผํ•จ)
  • drop ์ด๋ฒคํŠธ ์—ญ์‹œ ๋“œ๋กญ๋  ์š”์†Œ์—๋Š” e.preventDefault()๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์ •์ƒ์ ์ธ ๋™์ž‘์ด ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ
    ์ด๋ฒคํŠธ์— preventDefault() ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
  • ๋‹จ๋…์œผ๋กœ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ๋Š” ๋™์ž‘์„ ํ•˜์ง€ ์•Š์•˜๊ณ  dragover์ด๋ฒคํŠธ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ๋น„๋กœ์†Œ ๋™์ž‘์ด ๋œ๋‹ค.
// ์ฒ˜์Œ ๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ ์œ„์น˜ํ•˜๊ณ  ์žˆ๋Š” ์ขŒ์ธก ๋ฐ•์Šค ์˜์—ญ
const container = document.querySelector(".container");

container.addEventListener("dragover", (e) => {
  e.preventDefault();
  //console.log("๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ '์ฒซ' ๋ฒˆ์งธ ๋ฐ•์Šค ์˜์—ญ์— ๊ณ„์† ์œ„์น˜ํ•˜๋ฉด ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ");
});
container.addEventListener("drop", (e) => {
  e.preventDefault();
  console.log("๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ '์ฒซ' ๋ฒˆ์งธ ๋ฐ•์Šค ์˜์—ญ์— ๋“œ๋กญ");
});


// ๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ ์ด๋™ํ•˜์—ฌ ์œ„์น˜ํ•  ์šฐ์ธก ๋ฐ•์Šค ์˜์—ญ
const container2 = document.querySelector(".container2");

container2.addEventListener("dragover", (e) => {
  e.preventDefault();
  //console.log("๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ '๋‘' ๋ฒˆ์งธ ๋ฐ•์Šค ์˜์—ญ์— ๊ณ„์† ์œ„์น˜ํ•˜๋ฉด ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ");
});
container2.addEventListener("drop", (e) => {
  e.preventDefault();
  console.log("๋“œ๋ž˜๊ทธ ์š”์†Œ๊ฐ€ '๋‘' ๋ฒˆ์งธ ๋ฐ•์Šค ์˜์—ญ์— ๋“œ๋กญ");
});

See the Pen drag-drop-์—ฐ์Šต-drop by barzz12 (@inpaSkyrim) on CodePen.

 

dragend ์ด๋ฒคํŠธ

  • ์š”์†Œ์˜ ๋“œ๋ž˜๊ทธ๊ฐ€ ๋๋‚ ๋•Œ ๋ฐœ์ƒ(๋“œ๋ž˜๊ทธ ๋„์ค‘์— ๋งˆ์šฐ์Šค ๋ฒ„ํŠผ์„ ์•„๋ฌด๋ฐ๋‚˜ ๋†“์œผ๋ฉด)
const item = document.querySelector(".item");

item.addEventListener("dragend", (e) => {
  console.log(e)
  console.log("๋“œ๋ž˜๊ทธ๊ฐ€ ๋๋‚˜๋ฉด ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ");
});

See the Pen drag-drop-์—ฐ์Šต-dragend by barzz12 (@inpaSkyrim) on CodePen.


DataTransfer ๊ฐ์ฒด

๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ ์ด๋ฒคํŠธ๋ฅผ ์œ„ํ•œ ๋ชจ๋“  ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋ฉ”์†Œ๋“œ(event listener method)๋Š” DataTransfer ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ๋ฐ˜ํ™˜๋œ DataTransfer ๊ฐ์ฒด๋Š” ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ ๋™์ž‘์— ๊ด€ํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. 

item.addEventListener("drag", (event) => {
  console.log(event.dataTransfer); // ๋“œ๋ž˜๊ทธ ์ด๋ฒคํŠธ ์ •๋ณด๋ฅผ ๋‹ด๊ณ ์žˆ๋Š” dataTransfer ๊ฐ์ฒด
});

 

๋ฐ์ดํ„ฐ ์ „์†ก ๊ธฐ๋Šฅ์— ๊ด€ํ•œ ๋ฉ”์„œ๋“œ

์ด dataTransfer ๊ฐ์ฒด ๋‚ด์—์„œ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ ๋ฐ ๊ฐ€์ ธ์˜ค๊ธฐ, ์‚ญ์ œ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ํ‘œ์ค€ ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

๋ฉ”์†Œ๋“œ ์„ค๋ช…
event.dataTransfer.setData(format,data) ์ฒซ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ํฌ๋งท ๋ฌธ์ž์—ด์„ ์ง€์ •.
์ฒซ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜์— ์ง€์ •ํ•œ ํฌ๋งท๊ณผ ์ผ์น˜ํ•˜๋Š” ๊ฐ’์„ ๋‘๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ง€์ •.
๋‘๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ๋Š” ๋ฌธ์ž์—ด๋งŒ ์ง€์ • ๊ฐ€๋Šฅ
event.dataTransfer.getData(format) ์ฒซ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜์— ์ง€์ •ํ•œ ํฌ๋งท์˜ ์ „์†ก ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜.
์ง€์ •๋œ ํฌ๋งท์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์ง€์ •๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ๊ณต๋ฐฑ ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜
event.dataTransfer.clearData()
event.dataTransfer.clearData(format)
๋ฐ์ดํ„ฐ ์ „์†ก์šฉ์œผ๋กœ ์ง€์ •๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋‘ ์ œ๊ฑฐ.
์ฒซ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ํฌ๋งท ๋ฌธ์ž์—ด์„ ์ง€์ •ํ•˜๋ฉด ํ•ด๋‹น ํ˜•์‹๊ณผ ์ผ์น˜ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋งŒ์„ ์ œ๊ฑฐ.
event.dataTransfer.types dragstart ์ด๋ฒคํŠธ ๋ฐœ์ƒ์‹œ DOM ๋ชฉ๋ก์— ์žˆ๋Š” data format ์„ ์„ค์ •ํ•˜๋ฉฐ
setData ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ• ๋•Œ ์ง€์ •๋˜๋Š” format ๋ฌธ์ž์—ด์„ ๋ฐฐ์—ดํ˜•์‹์œผ๋กœ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

 

๋งŒ์ผ ํŒŒ์ผ์„ ๋“œ๋ž˜๊ทธ ๋“œ๋กญ ํ•˜๋Š” ๊ฒƒ์ด๋ผ๋ฉด, dataTrasfer์— files ํ”„๋กœํผํ‹ฐ๋กœ ์ ‘๊ทผํ•˜์—ฌ ํŒŒ์ผ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค

// ๋“œ๋ž˜๊ทธํ•œ ํŒŒ์ผ ๊ฐ์ฒด๊ฐ€ ํ•ด๋‹น ์˜์—ญ์— ๋†“์˜€์„ ๋•Œ
์š”์†Œ.ondrop = (e) => {
  e.preventDefault();

  // ๋“œ๋กญ๋œ ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ
  const files = [...e.dataTransfer?.files];
  
  console.log(files);
  
  // ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ ๋„์œ„๊ธฐ
  ์š”์†Œ.innerHTML = files.map(file => file.name).join("<br>");
}

javascript-drag-drop

See the Pen drag-drop-์™„์„ฑ์˜ˆ์ œ2-ํŒŒ์ผ by barzz12 (@inpaSkyrim) on CodePen.


์ปค์Šคํ…€ ๋“œ๋ž˜๊ทธ ๊ณ ์ŠคํŠธ ์ด๋ฏธ์ง€

์š”์†Œ๋ฅผ ๋งˆ์šฐ์Šค๋กœ ๋“œ๋ž˜๊ทธํ•˜๋ฉด ๊ทธ ์š”์†Œ์˜ ๋ชจ์Šต์ด ๊ณ ์ŠคํŠธ ์ด๋ฏธ์ง€(ghost image) ๋กœ์„œ ์ปค์„œ์— ๋”ธ๋ ค ๋‚˜์˜จ๋‹ค. ์ด ๋“œ๋ž˜๊ทธ ์ด๋ฏธ์ง€๋ฅผ DataTransfer.setDragImage() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž ์ปค์Šคํ…€์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

document.getElementById("drag-with-image").addEventListener("dragstart", function(e) {
    cosnt img = new Image();
    img.src = "์ด๋ฏธ์ง€ ๊ฒฝ๋กœ";
    
    // setDragImage(์ด๋ฏธ์ง€์š”์†Œ, xOffset, yOffset)
    e.dataTransfer.setDragImage(img, 150, 150); // ์‚ฌ์šฉํ•  ์‚ฌ์šฉ์ž ์ •์˜ ์ด๋ฏธ์ง€๋ฅผ ์„ค์ •
}, false);

See the Pen Custom Img ๋“œ๋ž˜๊ทธ ๋“œ๋กญ by barzz12 (@inpaSkyrim) on CodePen.


๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ ์˜ˆ์ œ ์ฝ”๋“œ ๋ชจ์Œ

 

๊ธฐ๋ณธ ์˜ˆ์ œ

  • dragstart ์ด๋ฒคํŠธํ•ธ๋“ค๋Ÿฌ์—์„œ e.dataTransfer.setData๋กœ ์ „๋‹ฌํ•  ๋ฐ์ดํ„ฐ๋ฅผ ์ง€์ •ํ•ด์ค€๋‹ค. 
  • setData๋Š” ํ‚ค-๊ฐ’ ํ˜•์‹์œผ๋กœ ์ €์žฅํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ํ‚ค๋งŒ ๋‹ค๋ฅด๋ฉด ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.
  • dragover์ด๋ฒคํŠธ์—์„œ๋Š” e.preventDefault()๋กœ drop ์ด๋ฒคํŠธ๊ฐ€ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๊ณ 
  • drop ์ด๋ฒคํŠธ์—์„œ e.dataTransfer.getData๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
<p id="drag" draggable="true">๋“œ๋ž˜๊ทธํ•ด๋ณด์„ธ์š”.</p>
<br>
<div id="drop">์—ฌ๊ธฐ์— ๋“œ๋กญํ•˜์„ธ์š”</div>

<script>
  document.getElementById('drag').ondragstart = function() {
    e.dataTransfer.setData('data', this.innerHTML); // ๋“œ๋ž˜๊ทธํ•ด๋ณด์„ธ์š” ๋ฌธ์ž์—ด ์ „๋‹ฌ
  };

  document.getElementById('drop').ondragover = function(e) {
    e.preventDefault(); // ํ•„์ˆ˜ ์ด ๋ถ€๋ถ„์ด ์—†์œผ๋ฉด ondrop ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  };
  document.getElementById('drop').ondrop = function() {
    alert(e.dataTransfer.getData('data')); // ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
  };
</script>

See the Pen html13-1 by Hyunyoung Cho (@zerocho) on CodePen.


๋“œ๋ž˜๊ทธ๋กœ ์š”์†Œ ์œ„์น˜ ๋ฐ”๊พธ๊ธฐ

<div class="container">
    <button class="draggable" draggable="true">๐ŸฆŠ</button>
    <button class="draggable" draggable="true">๐Ÿธ</button>
</div>
<div class="container">
    <button class="draggable" draggable="true">๐Ÿถ</button>
    <button class="draggable" draggable="true">๐Ÿฑ</button>
</div>

<script>
const draggables = document.querySelectorAll(".draggable");
const containers = document.querySelectorAll(".container");

draggables.forEach(draggable => {
  draggable.addEventListener("dragstart", () => {
    draggable.classList.add("dragging");
  });

  draggable.addEventListener("dragend", () => {
    draggable.classList.remove("dragging");
  });
});

containers.forEach(container => {
  container.addEventListener("dragover", e => {
    e.preventDefault();
    const afterElement = getDragAfterElement(container, e.clientX);
    const draggable = document.querySelector(".dragging");
    if (afterElement === undefined) {
      container.appendChild(draggable);
    } else {
      container.insertBefore(draggable, afterElement);
    }
  });
});

function getDragAfterElement(container, x) {
  const draggableElements = [
    ...container.querySelectorAll(".draggable:not(.dragging)"),
  ];

  return draggableElements.reduce(
    (closest, child) => {
      const box = child.getBoundingClientRect();
      const offset = x - box.left - box.width / 2;
      // console.log(offset);
      if (offset < 0 && offset > closest.offset) {
        return { offset: offset, element: child };
      } else {
        return closest;
      }
    },
    { offset: Number.NEGATIVE_INFINITY },
  ).element;
}
</script>

See the Pen drag-drop-1 by barzz12 (@inpaSkyrim) on CodePen.


<div class="container">
   <div class="draggable" draggable="true">
      <span class="ico-drag"></span>
      <div class="el">HTML</div>
   </div>
   <div class="draggable" draggable="true">
      <span class="ico-drag"></span>
      <div class="el">CSS</div>
   </div>
   <div class="draggable" draggable="true">
      <span class="ico-drag"></span>
      <div class="el">JavaScript</div>
   </div>
</div>

<div class="container">
   <div class="draggable" draggable="true">
      <span class="ico-drag"></span>
      <div class="el">React</div>
   </div>
   <div class="draggable" draggable="true">
      <span class="ico-drag"></span>
      <div class="el">Vue</div>
   </div>
   <div class="draggable" draggable="true">
      <span class="ico-drag"></span>
      <div class="el">Next JS</div>
   </div>
</div>

<script>
   /**
    * [x] ์—˜๋ฆฌ๋จผํŠธ์˜ .draggable, .container์˜ ๋ฐฐ์—ด๋กœ ์„ ํƒ์ž๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
    * [x] draggables๋ฅผ ์ „์ฒด๋ฅผ ๋ฃจํ”„ํ•˜๋ฉด์„œ dragstart, dragend๋ฅผ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
    * [x] dragstart, dragend ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒํ• ๋•Œ .dragging๋ผ๋Š” ํด๋ž˜์Šค๋ฅผ ํ† ๊ธ€์‹œํ‚จ๋‹ค.
    * [x] dragover ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๋™์•ˆ ๋งˆ์šฐ์Šค ๋“œ๋ž˜๊ทธํ•˜๊ณ  ๋งˆ์ง€๋ง‰ ์œ„์น˜ํ•ด๋†“์€ Element๋ฅผ ๋ฆฌํ„ดํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
    */

   (() => {
      const $ = (select) => document.querySelectorAll(select);
      const draggables = $('.draggable');
      const containers = $('.container');

      draggables.forEach(el => {
         el.addEventListener('dragstart', () => {
            el.classList.add('dragging');
         });

         el.addEventListener('dragend', () => {
            el.classList.remove('dragging')
         });
      });

      function getDragAfterElement(container, y) {
         const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]

         return draggableElements.reduce((closest, child) => {
            const box = child.getBoundingClientRect() //ํ•ด๋‹น ์—˜๋ฆฌ๋จผํŠธ์— top๊ฐ’, height๊ฐ’ ๋‹ด๊ฒจ์ ธ ์žˆ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด box๋ณ€์ˆ˜์— ํ• ๋‹น
            const offset = y - box.top - box.height / 2 //์ˆ˜์ง ์ขŒํ‘œ - top๊ฐ’ - height๊ฐ’ / 2์˜ ์—ฐ์‚ฐ์„ ํ†ตํ•ด์„œ offset๋ณ€์ˆ˜์— ํ• ๋‹น
            if (offset < 0 && offset > closest.offset) { // (์˜ˆ์™ธ ์ฒ˜๋ฆฌ) 0 ์ดํ•˜ ์™€, ์Œ์˜ ๋ฌดํ•œ๋Œ€ ์‚ฌ์ด์— ์กฐ๊ฑด
               return { offset: offset, element: child } // Element๋ฅผ ๋ฆฌํ„ด
            } else {
               return closest
            }
         }, { offset: Number.NEGATIVE_INFINITY }).element
      };

      containers.forEach(container => {
         container.addEventListener('dragover', e => {
            e.preventDefault()
            const afterElement = getDragAfterElement(container, e.clientY);
            const draggable = document.querySelector('.dragging')
            // container.appendChild(draggable)
            container.insertBefore(draggable, afterElement)
         })
      });
   })();
</script>

See the Pen drag-drop-์š”์†Œ์œ„์น˜ by barzz12 (@inpaSkyrim) on CodePen.


์ด๋ฏธ์ง€ ๋“œ๋ž˜๊ทธ ๋“œ๋กญ

<h1>๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ์„ ์ด์šฉํ•œ ๊ฐ์ฒด์˜ ์ด๋™</h1>
<p>๋ชจ๋‚˜๋ฆฌ์ž ๊ทธ๋ฆผ์„ ๋“œ๋ž˜๊ทธํ•ด์„œ ์˜†์˜ ์‚ฌ๊ฐํ˜•์œผ๋กœ ์˜ฎ๊ฒจ๋ณด์„ธ์š”!</p>
<div 
   ondrop="drop(event)" 
   ondragover="dragEnter(event)"
   >
   <img 
      id="monalisa" 
      width="180" height="280" 
      src="/examples/images/img_monalisa.png" 
      draggable="true" 
      ondragstart="drag(event)"
      >
</div>
<div 
   ondrop="drop(event)" 
   ondragover="dragEnter(event)"
   >
</div>
<script>
   function dragEnter(ev) {
   	ev.preventDefault();
   }
   
   function drag(ev) {
   	ev.dataTransfer.setData("text", ev.target.id);
   }
   
   function drop(ev) {
   	ev.preventDefault();
   	var data = ev.dataTransfer.getData("text"); // imgํƒœ๊ทธ ์•„์ด๋””๋ฅผ ๊ฐ€์ ธ์˜ด
   	ev.target.appendChild(document.getElementById(data)); // ๋‹ค๋ฅธ divํƒœ๊ทธ์— img๋ฅผ ์ถ”๊ฐ€ํ•จ(์˜ฎ๊น€. ๋“œ๋ž˜๊ทธ์ฒ˜๋ฆฌ)
   }
</script>

See the Pen drag&drop by barzz12 (@inpaSkyrim) on CodePen.


ํŒŒ์ผ ๋“œ๋ž˜๊ทธ ๋“œ๋กญ

<div id="drop">์—ฌ๊ธฐ์— ํŒŒ์ผ์„ ๋“œ๋žํ•˜์„ธ์š”</div>

<script>
  var drop = document.getElementById('drop');
  
  drop.ondragover = function(e) {
    e.preventDefault(); // ์ด ๋ถ€๋ถ„์ด ์—†์œผ๋ฉด ondrop ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  };
  
  drop.ondrop = function(e) {
    e.preventDefault(); // ์ด ๋ถ€๋ถ„์ด ์—†์œผ๋ฉด ํŒŒ์ผ์„ ๋ธŒ๋ผ์šฐ์ € ์‹คํ–‰ํ•ด๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.
    
    var data = e.dataTransfer;
    for (var i = 0; i < data.files.length; i++) {
      alert(data.files[i].name); // File API ์‚ฌ์šฉ
    }
  };
</script>

See the Pen ํŒŒ์ผ๋“œ๋ž by barzz12 (@inpaSkyrim) on CodePen.


<form>
   <input type="file" id="file" multiple>
   <div class="drop-zone">๋˜๋Š” ํŒŒ์ผ์„ ์—ฌ๊ธฐ๋กœ ๋“œ๋ž˜๊ทธํ•˜์„ธ์š”.</div>
</form>
<script>
   (function () {
      var $file = document.getElementById("file")
      var dropZone = document.querySelector(".drop-zone")
   
      var toggleClass = function (className) {
   
         console.log("current event: " + className)
   
         var list = ["dragenter", "dragleave", "dragover", "drop"]
   
         for (var i = 0; i < list.length; i++) {
            if (className === list[i]) {
               dropZone.classList.add("drop-zone-" + list[i])
            } else {
               dropZone.classList.remove("drop-zone-" + list[i])
            }
         }
      }
   
      var showFiles = function (files) {
         dropZone.innerHTML = ""
         for (var i = 0, len = files.length; i < len; i++) {
            dropZone.innerHTML += "<p>" + files[i].name + "</p>"
         }
      }
   
      var selectFile = function (files) {
         // input file ์˜์—ญ์— ๋“œ๋ž๋œ ํŒŒ์ผ๋“ค๋กœ ๋Œ€์ฒด
         $file.files = files
         showFiles($file.files)
   
      }
   
      $file.addEventListener("change", function (e) {
         showFiles(e.target.files)
      })
   
      // ๋“œ๋ž˜๊ทธํ•œ ํŒŒ์ผ์ด ์ตœ์ดˆ๋กœ ์ง„์ž…ํ–ˆ์„ ๋•Œ
      dropZone.addEventListener("dragenter", function (e) {
         e.stopPropagation()
         e.preventDefault()
   
         toggleClass("dragenter")
      })
   
      // ๋“œ๋ž˜๊ทธํ•œ ํŒŒ์ผ์ด dropZone ์˜์—ญ์„ ๋ฒ—์–ด๋‚ฌ์„ ๋•Œ
      dropZone.addEventListener("dragleave", function (e) {
         e.stopPropagation()
         e.preventDefault()
   
         toggleClass("dragleave")
      })
   
      // ๋“œ๋ž˜๊ทธํ•œ ํŒŒ์ผ์ด dropZone ์˜์—ญ์— ๋จธ๋ฌผ๋Ÿฌ ์žˆ์„ ๋•Œ
      dropZone.addEventListener("dragover", function (e) {
         e.stopPropagation()
         e.preventDefault()
   
         toggleClass("dragover")
      })
   
      // ๋“œ๋ž˜๊ทธํ•œ ํŒŒ์ผ์ด ๋“œ๋ž๋˜์—ˆ์„ ๋•Œ
      dropZone.addEventListener("drop", function (e) {
         e.preventDefault()
   
         toggleClass("drop")
   
         var files = e.dataTransfer && e.dataTransfer.files
         console.log(files)
   
         if (files != null) {
            if (files.length < 1) {
               alert("ํด๋” ์—…๋กœ๋“œ ๋ถˆ๊ฐ€")
               return
            }
            selectFile(files)
         } else {
            alert("ERROR")
         }
   
      })
   })();
</script>

See the Pen ํŒŒ์ผ๋“œ๋ž˜๊ทธ๋“œ๋กญ2 by barzz12 (@inpaSkyrim) on CodePen.


ํŒŒ์ผ ์—…๋กœ๋“œ & ํ”„๋ฆฌ๋ทฐ

<main class="container">
   <label class="label" id="label" for="input">
      <div class="inner" id="inner">๋“œ๋ž˜๊ทธํ•˜๊ฑฐ๋‚˜ ํด๋ฆญํ•ด์„œ ์—…๋กœ๋“œ</div>
   </label>
   <input id="input" class="input" accept="image/*" type="file" required="true" multiple="true" hidden="true">
   <p class="preview-title">preview</p>
   <div class="preview" id="preview"></div>
</main>

<script>
   var input = document.getElementById("input");
   var initLabel = document.getElementById("label");

   input.addEventListener("change", (event) => {
      const files = changeEvent(event);
      handleUpdate(files);
   });

   initLabel.addEventListener("mouseover", (event) => {
      event.preventDefault();
      const label = document.getElementById("label");
      label?.classList.add("label--hover");
   });

   initLabel.addEventListener("mouseout", (event) => {
      event.preventDefault();
      const label = document.getElementById("label");
      label?.classList.remove("label--hover");
   });

   document.addEventListener("dragenter", (event) => {
      event.preventDefault();
      console.log("dragenter");
      if (event.target.className === "inner") {
         event.target.style.background = "#616161";
      }
   });

   document.addEventListener("dragover", (event) => {
      console.log("dragover");
      event.preventDefault();
   });

   document.addEventListener("dragleave", (event) => {
      event.preventDefault();
      console.log("dragleave");
      if (event.target.className === "inner") {
         event.target.style.background = "#3a3a3a";
      }
   });

   document.addEventListener("drop", (event) => {
      event.preventDefault();
      console.log("drop");
      if (event.target.className === "inner") {
         const files = event.dataTransfer?.files;
         event.target.style.background = "#3a3a3a";
         handleUpdate([...files]);
      }
   });

   function changeEvent(event) {
      const { target } = event;
      return [...target.files];
   };

   function handleUpdate(fileList) {
      const preview = document.getElementById("preview");

      fileList.forEach((file) => {
         const reader = new FileReader();
         reader.addEventListener("load", (event) => {
            const img = el("img", {
               className: "embed-img",
               src: event.target?.result,
            });
            const imgContainer = el("div", { className: "container-img" }, img);
            preview.append(imgContainer);
         });
         reader.readAsDataURL(file);
      });
   };

   function el(nodeName, attributes, ...children) {
      const node =
         nodeName === "fragment"
            ? document.createDocumentFragment()
            : document.createElement(nodeName);

      Object.entries(attributes).forEach(([key, value]) => {
         if (key === "events") {
            Object.entries(value).forEach(([type, listener]) => {
               node.addEventListener(type, listener);
            });
         } else if (key in node) {
            try {
               node[key] = value;
            } catch (err) {
               node.setAttribute(key, value);
            }
         } else {
            node.setAttribute(key, value);
         }
      });

      children.forEach((childNode) => {
         if (typeof childNode === "string") {
            node.appendChild(document.createTextNode(childNode));
         } else {
            node.appendChild(childNode);
         }
      });

      return node;
   }
</script>

See the Pen image-drag-and-drop by an (@YWTechIT) on CodePen.


๋“œ๋ž˜๊ทธ ๊ฐ์ฒด ์ด๋™

<div id="outerContainer">
   <div id="container">
      <div id="item">

      </div>
   </div>
</div>

<script>
   var dragItem = document.querySelector("#item");
   var container = document.querySelector("#container");

   var active = false;
   var currentX;
   var currentY;
   var initialX;
   var initialY;
   var xOffset = 0;
   var yOffset = 0;

   container.addEventListener("touchstart", dragStart, false);
   container.addEventListener("touchend", dragEnd, false);
   container.addEventListener("touchmove", drag, false);

   container.addEventListener("mousedown", dragStart, false);
   container.addEventListener("mouseup", dragEnd, false);
   container.addEventListener("mousemove", drag, false);

   function dragStart(e) {
      if (e.type === "touchstart") {
         initialX = e.touches[0].clientX - xOffset;
         initialY = e.touches[0].clientY - yOffset;
      } else {
         initialX = e.clientX - xOffset;
         initialY = e.clientY - yOffset;
      }

      if (e.target === dragItem) {
         active = true;
      }
   }

   function dragEnd(e) {
      initialX = currentX;
      initialY = currentY;

      active = false;
   }

   function drag(e) {
      if (active) {

         e.preventDefault();

         if (e.type === "touchmove") {
            currentX = e.touches[0].clientX - initialX;
            currentY = e.touches[0].clientY - initialY;
         } else {
            currentX = e.clientX - initialX;
            currentY = e.clientY - initialY;
         }

         xOffset = currentX;
         yOffset = currentY;

         setTranslate(currentX, currentY, dragItem);
      }
   }

   function setTranslate(xPos, yPos, el) {
      el.style.transform = "translate3d(" + xPos + "px, " + yPos + "px, 0)";
   }
</script>

See the Pen drag-drop-๊ฐ์ฒด์ด๋™ by barzz12 (@inpaSkyrim) on CodePen.


<p>Drag the ball.</p>

<img src="https://en.js.cx/clipart/soccer-gate.svg" id="gate" class="droppable">

<img src="https://en.js.cx/clipart/ball.svg" id="ball">

<script>
  // ์ž ์žฌ์  ๋“œ๋กญ ๊ฐ€๋Šฅํ•œ ์š”์†Œ
   let currentDroppable = null;

   ball.onmousedown = function (event) {

      let shiftX = event.clientX - ball.getBoundingClientRect().left;
      let shiftY = event.clientY - ball.getBoundingClientRect().top;

      ball.style.position = 'absolute';
      ball.style.zIndex = 1000;
      document.body.append(ball);

      moveAt(event.pageX, event.pageY);

      function moveAt(pageX, pageY) {
         ball.style.left = pageX - shiftX + 'px';
         ball.style.top = pageY - shiftY + 'px';
      }

      function onMouseMove(event) {
         moveAt(event.pageX, event.pageY);

         ball.hidden = true;
         let elemBelow = document.elementFromPoint(event.clientX, event.clientY); // elemBelow : ๋“œ๋กญํ•  ์ˆ˜ ์žˆ๋Š” ๊ณต์˜ ์•„๋ž˜ ์š”์†Œ, ์ถ•๊ตฌ๊ณจ๋Œ€
         ball.hidden = false;

         // ๊ณต์„ ์œˆ๋„์šฐ ๋ฐ–์œผ๋กœ ๋“œ๋ž˜๊ทธ ํ–ˆ์„ ๋•Œ,
         // clientX, clientY ๊ฐ€ ์œˆ๋„์šฐ ๋ฐ–์— ์žˆ์œผ๋ฉด, elementFromPoint ๋Š” null ๋ฐ˜ํ™˜
         if (!elemBelow) return;

         // ์ž ์žฌ์ ์œผ๋กœ ๋“œ๋กญํ•  ์ˆ˜ ์žˆ๋Š” ์š”์†Œ๋ฅผ 'droppable' ํด๋ž˜์Šค๋กœ ์ง€์ •ํ•œ๋‹ค.  ์ถ•๊ตฌ ๊ณจ๋Œ€
         let droppableBelow = elemBelow.closest('.droppable');
        
         // ๋‘˜๋‹ค null ์ผ ์ˆ˜ ์žˆ๋‹ค. ๋“ค์–ด์˜ค๊ฑฐ๋‚˜, ๋‚˜๊ฐ€๊ฑฐ๋‚˜์— ํ•ด๋‹นํ•  ๋•Œ,
         // currentDroppale = null : ์ถ•๊ตฌ ๊ณจ๋Œ€ ๋ฐ–์— ์žˆ์„ ๋•Œ,
         // droppableBelow = null : ์ด๋ฒคํŠธ ๋™์•ˆ ์ถ•๊ตฌ ๊ณจ๋Œ€ ์•ˆ์— ์—†์„ ๋•Œ,  
         if (currentDroppable != droppableBelow) {
            if (currentDroppable) { 
               // ์ถ•๊ตฌ ๊ณจ๋Œ€ ๋ฐ–์— ์žˆ์„ ๋•Œ ๊ฐ•์กฐ ์ œ๊ฑฐ
               leaveDroppable(currentDroppable);
            }
            currentDroppable = droppableBelow;
            if (currentDroppable) { // null if we're not coming over a droppable now
               // ์ถ•๊ตฌ ๊ณจ๋Œ€ ์•ˆ์œผ๋กœ ๋“ค์–ด์˜ค๋Š” ๊ฒƒ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋กœ์ง
               enterDroppable(currentDroppable);
            }
         }
      }

      document.addEventListener('mousemove', onMouseMove);

      ball.onmouseup = function () {
         document.removeEventListener('mousemove', onMouseMove);
         ball.onmouseup = null;
      };

   };

   function enterDroppable(elem) {
      elem.style.background = 'pink';
   }

   function leaveDroppable(elem) {
      elem.style.background = '';
   }

   ball.ondragstart = function () {
      return false;
   };
</script>

See the Pen Untitled by barzz12 (@inpaSkyrim) on CodePen.


๋“œ๋ž˜๊ทธ & ๋“œ๋กญ JS ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ์„ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๊ธฐ์—๋Š” ์•„์ง ์‹ค๋ ฅ์ด ๋ถ€์กฑํ•˜๋‹ค๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ๋„ ํ•˜๋‚˜์˜ ๋ฐฉ๋ฒ•์ด๋‹ค.

๋Œ€ํ‘œ์ ์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋‘๊ฐ€์ง€๋ฅผ ์†Œ๊ฐœํ•ด๋ณธ๋‹ค.

 

๋“œ๋ž˜๊ทธ ๋“œ๋กญ ์ •๋ ฌ - Sortable.js

Sortable์€ ๊ธฐ๋ณธ HTML5 ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ์ฒด ๋ชฉ๋ก์„ ์ •๋ ฌํ•˜๋Š” ๊ฐ€๋ณ๊ณ  ๊ฐ„๋‹จํ•œ ๋ชจ๋“ˆ์ด๋‹ค.

๋ชจ๋“  ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ € ๋ฐ ํ„ฐ์น˜ ์žฅ์น˜์™€ ํ˜ธํ™˜ ๋œ๋‹ค.

 

GitHub - SortableJS/Sortable: Reorderable drag-and-drop lists for modern browsers and touch devices. No jQuery or framework requ

Reorderable drag-and-drop lists for modern browsers and touch devices. No jQuery or framework required. - GitHub - SortableJS/Sortable: Reorderable drag-and-drop lists for modern browsers and touch...

github.com

<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js"></script>
const columns = document.querySelectorAll(".column");

columns.forEach((column) => {
    new Sortable(column, {
        group: "shared",
        animation: 150,
        ghostClass: "blue-background-class"
    });
});

See the Pen drag-drop-์™„์„ฑ์˜ˆ์ œ3-sortable by barzz12 (@inpaSkyrim) on CodePen.


ํŒŒ์ผ/์ด๋ฏธ์ง€ ๋“œ๋ž˜๊ทธ ๋“œ๋กญ - Dropzone.js

๊ณ ํ€„๋ฆฌํ‹ฐ์˜ ๋“œ๋ž˜๊ทธ๋“œ๋กญ ๋ฉ”๋‰ด๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ• ์ˆ˜์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•ด์„  ๋‹ค์Œ ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ  ๋ฐ”๋ž€๋‹ค.

 

[Dropzone] ๐Ÿ“š ์ด๋ฏธ์ง€ & ํŒŒ์ผ ์—…๋กœ๋“œ (๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ) ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

Dropzone.js Dropzone.js๋Š” ํ”„๋ก ํŠธ ํŽ˜์ด์ง€์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€ํ‘œ์ ์ธ ํŒŒ์ผ ์—…๋กœ๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๋ฉฐ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ธฐ๋ณธ ์Šคํƒ€์ผ ๋˜ํ•œ ๋™์ ์ธ ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ๊ณ ๊ธ‰

inpa.tistory.com

See the Pen dropzone.js sample-1 by barzz12 (@inpaSkyrim) on CodePen.


# ์ฐธ๊ณ ์ž๋ฃŒ

https://ko.javascript.info/mouse-drag-and-drop

https://www.zerocho.com/category/HTML&DOM/post/5942c4ed858a010018a8c32f

https://ezerror.com/ko/%EB%B0%94%EB%8B%90%EB%9D%BC-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-%EB%93%9C%EB%9E%98%EA%B7%B8-%EC%95%A4-%EB%93%9C%EB%A1%AD

https://gingerkang.tistory.com/127

https://gurtn.tistory.com/143