Todo List (with JS)

ZYE KIM·2024년 3월 14일
0

코딩테스트 회고

사진 참고해 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();
});

✨완성✨

🤜⚡️🤛 강의만 따라하지 말기!

profile
주니어 프론트엔드개발자

0개의 댓글