30개의 프로젝트로 배우는 프론트엔드 with VanillaJS (9-2) To-Do-List

productuidev·2022년 10월 25일
0

FE Study

목록 보기
66/67
post-thumbnail

30개의 프로젝트로 배우는 프론트엔드 with VanillaJS (9-2) To-Do-List

(9) To-Do-List

04-2) 할일 삭제 기능

  • todo 내의 버튼이 추가될 때마다 이벤트를 계속 추가하면 비효율적
  • 이벤트버블링화를 위해서 todo 안에서 click(버튼이 눌린 것)을 감지하면 버튼에 맞는 실행을 하는 방향으로 구현 (이벤트를 한번만 걸어서 나중에 동적으로 요소가 추가되도 다시 이벤트를 걸 필요가 없는 방법으로 구현)
  • onClickTodoList : event target의 button을 찾아서 해당 id에 매칭될 때 이벤트 타겟 걸기
  • deleteTodo(target) : todoDiv는 target에서 todo 클래스를 탐색하고 styles.scss에 건 transition이 끝나면 삭제 (삭제되면서 todo에 delete 클래스가 추가되고 사라짐)\
  • 추가사항 : 바로 삭제되지 않도록 정말 삭제할 것인지 comfirm alert 추가

index.js

 addEvent() {
    this.addBtnEl.addEventListener('click', this.onClickAddBtn.bind(this));
    this.todoListEl.addEventListener('click', this.onClickTodoList.bind(this));
  }
  
onClickTodoList(event) {
    const { target } = event;
    const btn = target.closest('button');
    if (btn.matches('#delete-btn')) {
      this.deleteTodo(target);
    }
  }
  
deleteTodo(target) {
    const todoDiv = target.closest('.todo');
    if (window.confirm('정말 삭제하시겠습니까?')) {
      todoDiv.addEventListener('transitionend', () => {
        todoDiv.remove();
      });
    }
    todoDiv.classList.add('delete');
}

style.scss

.todo {
	transition: 1s ease;
}

참고

05-1) 할일 수정 기능

  • 할일 추가 상태에서 추가된 목록을 누르면 다음의 에러가 뜸 (매칭되는 값이 null인 경우)

  • onClickTodoList에 btn 매칭이 null값이 때의 조건 추가 (그냥 return 시키기)

index.js

onClickTodoList(event) {
    const { target } = event;
    const btn = target.closest('button');

    // btn이 null일 때의 조건 추가
    // console.log(btn);
    if (!btn) return; // = false
    
     ...
     
}
  • 수정 기능의 경우 위의 삭제와 동일하게 btn의 삭제 id를 매칭시켜서 찾은 후 이벤트 target 걸기
  • else if 조건
  • todo 클래스 찾은 후 할일이 입력되있는 input을 찾은 후 readOnly를 풀어서 focus가 가게한 후 edit 클래스 추가하여 input 활성화상태로 변경시키기
  • 수정이 활성 상태이면 style.scss에 지정한대로 저장 버튼만 보이게 됨

index.js

onClickTodoList(event) {
    const { target } = event;
    const btn = target.closest('button');
    
    if (!btn) return;

    if (btn.matches('#delete-btn')) {
      this.deleteTodo(target);
    } else if (btn.matches('#edit-btn')) {
      this.editTodo(target);
    }
  }
  
 editTodo(target) {
    const todoDiv = target.closest('.todo');
    const todoInputEl = todoDiv.querySelector('input');
    todoInputEl.readOnly = false;
    todoInputEl.focus();
    todoDiv.classList.add('edit');
  }

05-2) 할일 완료 기능

  • 수정한 상태를 저장하는 기능
  • 실제 저장된 값을 가져오는 것은 이후 localStorage에서 다뤄볼 예정으로 현재는 현재 활성상태에서 변경된 값을 표시하는 것을 구현
  • 수정 후 추가되었던 edit 클래스를 제거하고 readOnly로 다시 변경 (비활성화 true)

index.js

onClickTodoList(event) {
    const { target } = event;
    const btn = target.closest('button');
    
    if (!btn) return;

    if (btn.matches('#delete-btn')) {
      this.deleteTodo(target);
    } else if (btn.matches('#edit-btn')) {
      this.editTodo(target);
    } else if (btn.matches('#save-btn')) {
      this.saveTodo(target);
    }
  }
  
saveTodo(target) {
    const todoDiv = target.closest('.todo');
    todoDiv.classList.remove('edit');

    const todoInputEl = todoDiv.querySelector('input');
    todoInputEl.readOnly = true;
  }

05-3) 완료 할일 체크 기능

  • 할일이 완료되면 체크 상태로 표시하는 기능
  • 체크 상태로 표시는 style.scss에 지정한 대로 할일에 밑줄이 그어지고 opacity가 낮춰짐

index.js

onClickTodoList(event) {
    const { target } = event;
    const btn = target.closest('button');
    
    if (!btn) return;

    if (btn.matches('#delete-btn')) {
      this.deleteTodo(target);
    } else if (btn.matches('#edit-btn')) {
      this.editTodo(target);
    } else if (btn.matches('#save-btn')) {
      this.saveTodo(target);
    } else if (btn.matches('#complete-btn')) {
      this.completeTodo(target);
    }
  }
  
  completeTodo(target) {
    const todoDiv = target.closest('.todo');
    todoDiv.classList.toggle('done');
  }

06) 필터 기능

  • ALL(전체)/TODO(예정)/DONE(완료) : index.html의 input-container 내에 radio-area
  • 해당 btn의 name과 value 속성을 활용하여 버튼 탐색 후 할일 상태별 필터링하는 이벤트 걸기
  • filterTodo(status) : btn value 속성값이 status가 되서 switch-case문을 활용하여 해당 상태에 따라 flex/done 클래스 포함 여부에 따라 목록에 표시

index.html

        <div class="radio-area" id="radio-area">
          <input type="radio" id="filter1" name="filter" value="ALL" />
          <label for="filter1">All</label>
          <input type="radio" id="filter2" name="filter" value="TODO" />
          <label for="filter2">Todo</label>
          <input type="radio" id="filter3" name="filter" value="DONE" />
          <label for="filter3">Done</label>
        </div>

index.js

addEvent() {
    this.addBtnEl.addEventListener('click', this.onClickAddBtn.bind(this));
    this.todoListEl.addEventListener('click', this.onClickTodoList.bind(this));
    this.addRadioBtnEvent();
  }
  
  // 할일 상태별 필터링 이벤트
  addRadioBtnEvent() {
    for (const filterRadioBtnEl of this.filterRadioBtnEls) {
      filterRadioBtnEl.addEventListener(
        'click',
        this.onClickRadioBtn.bind(this),
      );
    }
  }

  // radio btn 이벤트
  onClickRadioBtn(event) {
    const { value } = event.target;
    console.log(value);
    this.filterTodo(value);
  }

  // filtering
  filterTodo(status) {
    const todoDivEls = this.todoListEl.querySelectorAll('div.todo');
    for (const todoDivEl of todoDivEls) {
      switch (status) {
        case 'ALL':
          todoDivEl.style.display = 'flex';
          break;
        case 'DONE':
          todoDivEl.style.display = todoDivEl.classList.contains('done')
            ? 'flex'
            : 'none';
          break;
        case 'TODO':
          todoDivEl.style.display = todoDivEl.classList.contains('done')
            ? 'none'
            : 'flex';
          break;
      }
    }

참고자료

중간 결과


Jira같네..?!

profile
필요한 내용을 공부하고 저장합니다.

0개의 댓글