- 요소 검색 → querySelector
- 상호작용 만들기 → addEventListener
- CSS 변경 → style 속성(property)
HTML
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="author" content="">
<meta name="description" content="">
<meta http-equiv="X-UA-conpatible" content="IE-edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ToDoList</title>
<!-- S : FONT -->
<link href="https://fonts.googleapis.com/css?family=Noto+Sans+KR:100,300,400,500,700,900&display=swap"
rel="stylesheet">
<!--// E : FONT -->
<!-- S : CSS -->
<link rel="stylesheet" href="css/reset.css">
<link rel="stylesheet" href="css/index.css">
<!-- //E : CSS -->
</head>
<body>
<div class="wrap">
<div class="container">
<div class="title">
<h1>Todo List</h1>
</div>
<div class="todo-form form"></div>
<ul class="todo-list"></ul>
</div>
</div>
<!-- S : JS -->
<script type="text/javascript" src="js/todos.js"></script>
<!--//E : JS -->
</body>
</html>
CSS
html { height:100%; overflow:hidden; }
body { height:100%; background-color:rgba(153, 153, 153); color:rgba(153, 153, 153); }
.wrap > *{ transition: all 0.5s ease-in-out; }
.wrap { height:100%; text-align:center; display:flex; justify-content:center; align-items:center; }
.container { min-width:400px; min-height:500px; height:auto; padding:1rem 0.5rem; background-color:#fff; border:1px solid #fff; border-radius: 8px; display: flex; align-items: center; flex-direction: column; justify-content: flex-start; }
.title h4 { font-size:1.5rem; color:rgba(153, 153, 153); font-weight:700; text-align:left; }
.title h1 { line-height:1.2; color:rgba(153, 153, 153); font-size:4.5rem; font-weight:700; text-align:left; /*background: linear-gradient(to right top, rgba(116, 71, 71, 0.5), rgba(153, 153, 153)); color: transparent; -webkit-background-clip: text; transition: all 0.5s ease-in-out;*/ }
.form { width:78%; margin:.3rem 0; }
.form > form { display:flex; justify-content:space-between; }
.form > form > * { line-height:1.5; padding:.25rem; outline:0; border:1px solid rgba(153, 153, 153); border-radius: 3px; }
.form input { width:80%; }
.form button { width:15%; padding:.25rem .5rem; background-color:rgba(153, 153, 153); color:#fff; }
.form button:hover { background-color:rgb(116, 71, 71, 0.5); border:1px solid transparent; color:#fff; }
.todo-list{ width:78%; margin-top:10px; }
.todo-list li{text-align: left; }
@media (max-width: 400px) and (orientation: portrait) {
.container { min-width:90%; }
.title h1 { font-size:3.2rem; }
.form { width:85%; margin: 0 0; }
.form > *{ padding: .18rem; }
.form input { width:78%; }
.form button{ width:20%; padding:.18rem .2rem; }
}
JS
'use strict';
/*
1. 폼 div 가져오기
2. 전체 실행 함수 만들기(init)
3. 인풋 만들기
4. 저장
5. 추가
6. 삭제
7. 인풋비우기
*/
//1 폼 가져오기
const todoForm = document.querySelector('.todo-form');
const todoList = document.querySelector('.todo-list');
let toDoArr = [];
//8 투두리스트 배열 저장하기
function saveList() {
const stringList = JSON.stringify(toDoArr); // 문자열 변환
localStorage.setItem('todos', stringList);
}
// console.log(todoForm);
//3 인풋 그리기
function drowTodo() {
const form = document.createElement('form');
const input = document.createElement('input');
const btn = document.createElement('button');
input.placeholder = '오늘 할 일을 입력해 주세요.';
input.type = 'text';
input.className = 'todo-input';
btn.innerText = '추가';
// 탭/엔터키 이벤트 작동 코드
form.addEventListener('keydown', (event) => {
let key = event.key || event.keyCode;
if (key === 'Tab' || key === 9) {
btn.style.cssText = `
background-color:rgba(116, 71, 71, 0.5);
border:1px solid transparent;
`;
}
if (key === 'Enter' || key === 13) {
form.addEventListener('submit', handleTodo);
if (input.value === '') {
alert('할일을 작성해 주세요.');
event.preventDefault();
}
}
});
// 인풋 엔터 제한
input.addEventListener('keydown', (event) => {
let key = event.key || event.keyCode;
if (key === 'Enter' || key === 13) event.preventDefault();
});
// 클릭 작동 코드
btn.addEventListener('click', () => {
form.addEventListener('submit', handleTodo);
});
todoForm.appendChild(form);
form.appendChild(input);
form.appendChild(btn);
}
//7 투두리스트 완료 함수
function finishList(event) {
const li = event.target.parentElement;
li.style.textDecoration = 'line-through';
}
//6 투두리스트 삭제 함수
function deleteList(event) {
// console.log(event.target.parentElement);
// console.dir(event.target.parentElement.innerText);
const li = event.target.parentElement;
toDoArr = toDoArr.filter((todo) => todo.id !== parseInt(li.id)); //todo 아이디는 넘버, li아이디는 string이라 지워지지 않음(parseInt로 바꿔주기)
li.remove();
saveList(); //다시 한번로컬 스토리지 호출
}
//5 투두리스트 추가하기 함수
function addList(textObj) {
// console.log(`아 써지게 할꺼야 ${text}`);
// addList가 그려질 때마다 array에 push
const li = document.createElement('li');
const span = document.createElement('span');
const delBtn = document.createElement('button');
const chkBtn = document.createElement('button');
li.id = textObj.id;
li.className = 'data';
delBtn.className = 'btn-delete';
chkBtn.className = 'btn-check';
delBtn.innerText = `❌`;
chkBtn.innerText = `✔️`;
span.innerText = textObj.text;
delBtn.addEventListener('click', deleteList);
chkBtn.addEventListener('click', finishList);
li.appendChild(span);
li.appendChild(chkBtn);
li.appendChild(delBtn);
todoList.appendChild(li);
}
//4 인풋 작동 함수
function handleTodo(event) {
event.preventDefault();
const form = event.target;
const input = form.querySelector('input');
const newValue = input.value;
input.value = '';
//투두리스트 객체로 바꾸기
const newValueObj = {
id: Date.now(),
text: newValue,
};
toDoArr.push(newValueObj);
// addList(newValue); 배열의 값을 객체로 바꾸고 나서 [object Object]로 뜨는 것을 방지 위해
addList(newValueObj);
saveList();
}
//9 로딩
function loading() {
const loadedList = localStorage.getItem('todos');
if (loadedList !== null) {
const parsedList = JSON.parse(loadedList);
toDoArr = parsedList;
//객체를 id와 text로 풀기
parsedList.forEach(addList);
}
drowTodo();
return;
}
//2 종합 함수 만들기
function init() {
loading();
}
init();
:
:
(출처 : 구조 분해 할당)
(출처 : Array.prototype.filter())
(출처 : Array.prototype.map())
(참고할 것 : 다른 방식으로 풀이)
(참고할 것 : 다른 방식으로 풀이)
localStorage 전 버전 다시 만들어 보기
깃허브에 올리기
(바로가기 : 할 일 목록 만들기)