css
@charset "UTF-8";
:root { --bgWhite: #ffffff; --bgTrasW: rgba(255,255,255,0.7); --bgTrasW2: rgba(255,255,255,0.85); --bgPink: #ffd6e1; }
body { font-family: Verdana, Geneva, Tahoma, sans-serif; background: #3257f1; background: linear-gradient(151deg,#3257f1 20%, #bcfff5 60%, #ffffff 75%, #eca7df 95%, #f897d6 100%); }
.container { border: 5px solid red; display: flex; flex-direction: column; row-gap: 2em; height: 100vh; max-width: 1000px; overflow-y: auto; padding: 3rem 1rem; margin: auto; }
.container form { display: flex; column-gap: 2rem; }
.container input { width: 100%; height: 40px; outline: none; border: none; padding: 0 10px; border-radius: 4px; background: var(--bgTrasW2); }
.container input:focus { background: var(--bgWhite); }
.container button { width: 200px; height: 40px; border-radius: 4px; outline: none; border: none; cursor: pointer; background: var(--bgTrasW); transition: .3s; }
.container button:hover { background: var(--bgWhite); }
.blocks { border: 2px solid blue; flex: 1; display: grid; grid-template-columns: repeat(3, 1fr); column-gap: 3rem; }
.block { display: flex; flex-direction: column; row-gap: 1.5rem; background: var(--bgTrasW2); padding: 1.5rem; border-radius: 4px; box-shadow: 1px 1px 5px rgba(0,0,0,0.7); }
.list { border: 3px solid orange; flex: 1; display: flex; flex-direction: column; row-gap: 0.8rem; }
.item { background: var(--bgWhite); padding: 1rem; border-radius: 4px; box-shadow: 0 0 3px rgba(0,0,0,0.3); }
.item:hover { background: var(--bgPink); cursor: pointer; }
@media(max-width:750px) {
.blocks { display: flex; flex-direction: column; row-gap: 1rem; column-gap: 3rem; }
.block { flex: 1; }
}
html
<div class="container">
<form>
<input type="text" placeholder="할 일을 입력하세요" required>
<button type="submit">Add</button>
</form>
<div class="blocks">
<div class="block">
<h2>🚩 To do</h2>
<div id="todo" class="list">
<div id="" class="item">
입력내용1
</div>
</div>
</div>
<div class="block">
<h2>🚴♀️ Doing</h2>
<div id="doing" class="list">
</div>
</div>
<div class="block">
<h2>✅ Done</h2>
<div id="done" class="list">
</div>
</div>
</div>
</div>
js
const form = document.querySelector("form");
const boxes = document.querySelectorAll(".list");
// 출발, 이동(도착)시점의 .list의 id이름
let from, to;
// * 2-3. 배열 생성 (localStorage에 저장하기 위함)
let todoList = [];
let doingList = [];
let doneList = [];
let lists = { // 3개의 list들을 객체 형태로 묶음
// id이름을 배열의 이름과 맞춤 (key값과 id값이 같게)
todo: todoList, // ? id값을 받아옴?
doing: doingList,
done: doneList
};
// * 2-2. 각 list들 위에 올라갔을 때 발생하는 함수 정의
// .list가 드래그 되는 것을 인식
const dragOver = (event) => {
event.preventDefault(); // 마우스 커서 금지 표시 대신 +표시 생기게 함
// console.log("반응하는 list의 id는?", event.target.id);
// const targetId = event.target.id; // 아래와 같은 코드
const { id:targetId } = event.target; // 마우스가 지나가는 곳의 id를 가져옴
const listIds = Object.keys(lists); // 오브젝트의 키값만 가져옴
if (listIds.includes(targetId)) { // 마우스가 지나가는 곳 id가 셋 중 하나와 같을 떄만
to = targetId; // 도착(이동) 지점을 의미하는 to에 아이디를 넣는다.
}
console.log(to);
}
// * 2. 드래그 시작 시 발생하는 함수 정의
const dragStart = (event) => {
// console.log(event.target.parentElement.id);
from = event.target.parentElement.id; // 드래그 시작하는 지점. list의 id
console.log(from);
}
// * 2-4. 드래그 종료 시 발생하는 함수 정의. 다른 list로 옮긴 아이템을 삭제
const dragEnd = (event) => {
// console.log("드래그 끝");
if (from === to) { return; } // 같은 list 내부에서 움직일 때는 안 지워지게 함
event.target.remove(); // 출발점 list에서는 지워줌
const { id } = event.target; // 마우스가 지나가는 곳의 id를 가져옴
// * 2-6. 내가 지운 item의 아이디와 다른 item의 아이디가 같은 것이 있으면 안됨
lists[from] = lists[from].filter((item) => {
if (item.id !== id){
return item;
} else {
createElement(to, item);
}
})
// console.log(from, lists[from]);
// console.log(to, lists[to]);
// * 3. 로컬스토리지에 저장하는 함수 실행 (로컬스토리지 업데이트)
saveList(from);
saveList(to);
}
// * 5. 우클릭으로 삭제하는 함수 정의
const removeItem = (event) => {
event.preventDefault();
const { id } = event.target;
const { id:parentId } = event.target.parentElement;
// console.log(parentId);
event.target.remove(); // 이 코드만 작성하면 로컬스토리지에서는 삭제 안됨
// * 5-2. 로컬스토리지에서도 삭제하도록 하는 코드
lists[parentId] = lists[parentId].filter((aa) => {
return aa.id !== id;
})
saveList(parentId);
// console.log(parentId, lists[parentId]);
}
// * 1-2. item을 넣을 html 태그를 만드는 함수 정의
const createElement = (listId, newTodo) => { // listId = .list (html class), 아래 실행구에서 넘어온 newTodo
const list = document.querySelector(`#${listId}`); // todo, doing, done 중 뭐가 들어올지 모르기 때문
const item = document.createElement("div"); // div 생성
item.id = newTodo.id; // 받아온 id를 새로 만든 item의 id에 넣음
item.innerText = newTodo.text; // item 안에 글씨 넣음
item.classList.add("item"); // item 클래스 추가
// * 2. 드래그 기능 추가 (item을 만들 때 처음부터 가지고 있게 추가)
item.draggable = true; // 드래그 할 수 있는 기능 추가
item.addEventListener("dragstart", dragStart); // 드래그 시작 시 발생하는 함수(이벤트 인지)
item.addEventListener("dragend", dragEnd); // 드래그 종료 시 발생하는 함수(이벤트 인지)
// * 5. 우클릭으로 삭제하는 함수 실행
item.addEventListener("contextmenu", removeItem);
// * 1-3. 화면에 입력되도록 함
list.appendChild(item); // item을 부모의 마지막 자식으로(위치) 붙임
// * 2-5. 보낸 매개변수를 저장
lists[listId].push(newTodo) // 배열의 item 생성 후 배열에 넣어줌
// newTodo : id와 내가 입력한 값을 가져옴
}
// * 3. 로컬스토리지에 저장하는 함수 선언
const saveList = (aa) => { // 리스트의 아이디를 받아옴
localStorage.setItem(aa, JSON.stringify(lists[aa]));
}
// * 1. 새 할 일을 생성하는 함수. 입력값을 받아오고 새로운 아이템을 만들어주는 함수.
// 할 일. 오브젝트. id. 입력할 내용 등을 만드는곳.
const createTodo = (event) => {
event.preventDefault(); // 자동으로 새로고침되는 효과를 막아줌
// console.log("createTodo");
const input = document.querySelector("input");
const id = uuidv4(); // uuid라이브러리를 연결하여 사용 (id를 랜덤으로 부여)
// const text = input.value; // 입력 창 내용을 변수에 저장
// ? newTodo
const newTodo = { // object 안에 집어넣을 것
id, // 새로 만든 id값 // key: value가 같은 경우 축약 가능
text: input.value, // 받아온 value값 // 입력 창 내용을 저장
}
// console.log(newTodo.id, newTodo.text);
// * 1-2. 입력한 내용이 목록에 추가되도록 하는 곳. item을 넣을 html 태그를 만듦
createElement("todo", newTodo) // item을 만드는 함수 실행, 들어갈 list의 아이디는 todo
input.value = ""; // 입력 창 비우기
// * 3. 로컬스토리지에 저장하는 함수 실행 (로컬스토리지 업데이트)
saveList(todo);
}
// * 4. 로컬스토리지에 저장된 내용을 불러오는 함수 정의
const loadList = () => {
// key 단위로 불러옴
const userTodoList = JSON.parse(localStorage.getItem("todo"));
const userDoingList = JSON.parse(localStorage.getItem("doing"));
const userDoneList = JSON.parse(localStorage.getItem("done"));
if(userTodoList) {
userTodoList.forEach((aa) => { // userTodoList에서 가져옴 (todo key값에 해당하는 value)
createElement("todo", aa);
});
}
userDoingList && userDoingList.forEach((aa) => {
createElement("doing", aa);
});
userDoneList && userDoneList.forEach((aa) => {
createElement("done", aa);
});
};
// * 1. 아이템을 생성하는 함수 실행
form.addEventListener("submit", createTodo);
// form의 submit = 클릭, 엔터 등
// 무조건 새로고침이 한번 되기 때문에 preventDefault 무조건 작성
// * 2-2. 아이템이 움직일 때마다 각각 list의 id(위치)를 인식. 각 list의 위에 올라갔을 때 함수 실행
// boxes는 .list로 위에서 정의(list가 너무 많아서 헷갈릴 수 있기 때문)
boxes.forEach((box) => {
// 각 list(li)들에 addEventListener가 부여된것
box.addEventListener("dragover", dragOver);
})
// * 4. 로컬스토리지에 저장된 내용을 불러오는 함수 실행
loadList();