๐ ๋๋๊ทธ ์ค ๋๋กญ(Drag and Drop) ๊ธฐ๋ฅ ์ดํด & ๊ตฌํํ๊ธฐ
HTML ๋๋๊ทธ ์ค ๋๋กญ ์ฌ์ฉ๋ฒ
๋๋๊ทธ(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>");
}
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๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ฒด ๋ชฉ๋ก์ ์ ๋ ฌํ๋ ๊ฐ๋ณ๊ณ ๊ฐ๋จํ ๋ชจ๋์ด๋ค.
๋ชจ๋ ์ต์ ๋ธ๋ผ์ฐ์ ๋ฐ ํฐ์น ์ฅ์น์ ํธํ ๋๋ค.
<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
๊ณ ํ๋ฆฌํฐ์ ๋๋๊ทธ๋๋กญ ๋ฉ๋ด๋ฅผ ๊ฐ๋จํ๊ฒ ๊ตฌํํ ์์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
์ฌ์ฉ๋ฒ์ ๋ํด์ ๋ค์ ํฌ์คํ ์ ์ฐธ๊ณ ๋ฐ๋๋ค.
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