사진 참고해 todo List html, css, js로 40분안에 구현
통과여부는...👇
....
...
..
.
찾아요.. 쥐구멍...
기본적인건데, 구현하지 못하다니...(개발자는 무리인가)
마냥 좌절하구 있을 수 없어, 원인을 파악하고 해결해나가기로 했다.
또, 블로그에 기록해서 내 머리 속에 더 강력하게 남기기로 함!
vue와 react 강의통해 todo list를 많이 만들어봤는데,
가장 큰 문제는 혼자 구현해보지 않고, 기록도 안함.
결국 내 머리, 손가락에 남는건 없었던 것이다
problem 1 : list binding부터 실패 -> 마지막 하나만 나옴
추가되어도 추가된 마지막 하나만 나온다!
ul에 li을 생성해서 innerHTML 하였음 되었는데..잘못 타켓팅함
solution 1
const todoWrap = document.querySelector('.todoWrap');
let todos = [
{
id: 1,
text: '코테 회고',
completed: false,
},
{
id: 2,
text: '방청소하기',
completed: true,
}
];
const renderList = () => {
todoWrap.innerHTML = ''; // 초기화
todos.forEach(todo => {
// li 만들기
let item = document.createElement('li');
item.setAttribute('class','todoItem');
// start li 내부 요소 만들어 넣기 + 클래스명 + 이벤트리스너 추가
let checkBtn = document.createElement('button');
checkBtn.setAttribute('class','checkBtn');
checkBtn.addEventListener('click',() => checkTodo(todo.id));
let todoText = document.createElement('p');
todoText.innerText = todo.text;
let removeBtn = document.createElement('button');
removeBtn.setAttribute('class','removeBtn');
removeBtn.innerText = '삭제';
removeBtn.addEventListener('click', () => removeTodo(todo.id));
todo.completed ? item.classList.add('completed') : item.classList.remove('completed')
item.append(checkBtn);
item.append(todoText);
item.append(removeBtn);
// end li 내부 요소 만들어 넣기 + 클래스명 + 이벤트리스너 추가
// 만든 li ul에 넣기
todoWrap.appendChild(item);
})
}
renderList() // 호출하기!
problem 2 : createTodo
추가 기능은 문제 없었으니 보완해서 다시 해보기
solution 2
const todoInput = document.querySelector('.todoInput');
const addBtn = document.querySelector('.addBtn');
const createTodo = function(){
if(todoInput.value === '') {
alert('할 일을 입력하세요');
} else {
// new 객체 만들어 담기
let newItem = {
id: nextId + 1,
text: todoInput.value,
complted: false
};
// todos = [...todos,newItem]; 방법1
todos = todos.concat(newItem); //방법2
// reset
todoInput.value = '';
nextId += 1;
// render list
renderList();
}
}
// 버튼에 클릭이벤트 추가
addBtn.addEventListener('click',(e)=>{
e.preventDefault();
createTodo();
});
solution3: checkTodo + removeTodo
const checkTodo = function(id) {
let newTodos = todos.map(todo => {
if(todo.id === id) {
return {...todo,completed: !todo.completed}
}else return todo
});
todos = newTodos;
renderList();
}
const removeTodo = function(id) {
let newTodos = todos.filter(todo => todo.id !== id);
todos = newTodos;
renderList();
}
전체 코드 보기
html
<body>
<div class="wrap">
<h2>TodoList</h2>
<form>
<input type="text" class="todoInput" placeholder="할일을 입력하세요">
<button class="addBtn">추가</button>
</form>
<ul class="todoWrap">
</ul>
</div>
</body>
style.css
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
li {
list-style: none;
}
body {
display: flex;
justify-content: center;
align-items: center;
height: 100dvh;
background-color: #f8f8f8;
}
.wrap{
padding: 30px;
width: 450px;
min-height: 300px;
height: fit-content;
background-color: #fff;
border-radius: 25px;
}
form {
margin-top: 20px;
width: 100%;
display: flex;
align-items: center;
column-gap: 8px;
}
.addBtn {
position: relative;
font-size: 0;
width: 35px;
height: 35px;
border: 0;
background-color: #000;
}
.addBtn::before,
.addBtn::after {
content: '';
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: #fff;
border-radius: 5%;
}
.addBtn::before{
width: 18px;
height: 2px;
}
.addBtn::after{
width: 2px;
height: 18px;
}
.todoInput {
flex: auto;
border: 2px solid #000;
padding: 8px;
}
.todoWrap {
margin-top: 20px;
}
.todoWrap .todoItem:not(:first-of-type){
border-top: 1px solid #eee;
}
.todoItem {
padding: 10px;
display: flex;
column-gap: 8px;
align-items: center;
}
.todoItem p {
flex: auto
}
.checkBtn {
width: 25px;
height: 25px;
border-radius: 100%;
background-color: #fff;
border: 2px solid #000;
cursor: pointer;
}
.removeBtn {
padding: 8px 10px;
font-size: 16px;
width: fit-content;
border: 0;
border-radius: 5px;
cursor: pointer;
}
.todoItem.completed {
background-color: #f8f8f8;
}
.todoItem.completed .checkBtn{
position: relative;
background-color: #ddd;
border-color: #ddd;
}
.todoItem.completed p {
text-decoration: line-through;
color: #555;
}
.todoItem.completed .checkBtn:before{
content:'';
position: absolute;
left: 5%;
top: 30%;
transform: rotate(45deg) translate(0%, -50%);
width: 6px;
height: 10px;
border-right: 3px solid #fff;
border-bottom: 3px solid #fff;
border-radius: 2px;
}
index.js
'use strict';
const todoInput = document.querySelector('.todoInput');
const addBtn = document.querySelector('.addBtn');
const todoWrap = document.querySelector('.todoWrap');
let todos = [
{
id: 1,
text: '코테 회고',
completed: false,
},
{
id: 2,
text: '방청소하기',
completed: true,
}
];
let nextId = todos.length;
const checkTodo = function(id) {
let newTodos = todos.map(todo => {
if(todo.id === id) {
return {...todo,completed: !todo.completed}
}else return todo
})
todos = newTodos;
renderList();
}
const removeTodo = function(id) {
let newTodos = todos.filter(todo => todo.id !== id);
todos = newTodos;
renderList();
}
// list render..!
const renderList = () => {
todoWrap.innerHTML = '';
todos.forEach(todo => {
let item = document.createElement('li');
item.setAttribute('class','todoItem');
let checkBtn = document.createElement('button');
checkBtn.setAttribute('class','checkBtn');
checkBtn.addEventListener('click',() => checkTodo(todo.id));
let todoText = document.createElement('p');
todoText.innerText = todo.text;
let removeBtn = document.createElement('button');
removeBtn.setAttribute('class','removeBtn');
removeBtn.innerText = '삭제';
removeBtn.addEventListener('click', () => removeTodo(todo.id));
todo.completed ? item.classList.add('completed') : item.classList.remove('completed')
item.append(checkBtn);
item.append(todoText);
item.append(removeBtn);
todoWrap.appendChild(item);
});
}
renderList();
const createTodo = function(){
if(todoInput.value === '') {
alert('할 일을 입력하세요');
} else {
let newItem = {
id: nextId + 1,
text: todoInput.value,
complted: false
};
// todos = [...todos,newItem];
todos = todos.concat(newItem);
// reset
todoInput.value = '';
nextId += 1;
// render list
renderList();
}
}
addBtn.addEventListener('click',(e)=>{
e.preventDefault();
createTodo();
});
✨완성✨
🤜⚡️🤛 강의만 따라하지 말기!