간단한 도서관 앱

소개



HTML, CSS 및 JavaScript로 만든 TheOdinProject 커리큘럼의 프로젝트입니다.

Live Demo | Github Repo

목적



다음과 같이 예상되는 작은 도서관 앱을 만듭니다.
  • 책 개체를 만드는 생성자입니다.
  • 사용자의 입력을 받아 새 책 개체를 배열에 저장할 수 있는 함수
  • 책을 보관하는 배열

  • let myLibrary = [];
    
    function Book() {
      // the constructor
    }
    
    function addBookToLibrary() {
      // do stuff
    }
    


    HTML 및 CSS 정보



    Spectre-CSS 프레임워크를 사용하여 테이블과 양식을 만든 템플릿을 제공하겠습니다. 정말 간단하고 이 라이브러리 앱을 만드는 용도로만 사용되는 겸손한 레이아웃입니다.

    이번 챌린지에서 저는 JavaScript에 정말 관심이 많았습니다.

    실습



    먼저 생성자 함수를 완성하기 시작했습니다.

    도서 생성자




    function Book(title, author, pages, status) {
      const id = Math.floor(Math.random() * 10000);
    
      this.title = title;
      this.author = author;
      this.pages = pages;
      this.status = status;
      this.id = id;
    }
    


    여기서 전체 아이디어는 각 책 항목에 대해 고유한 ID를 만드는 것입니다.
    왜요? 나중에 배열에서 고유한 항목을 삭제하는 기능을 추가하고 싶기 때문입니다. 배열에서 인덱스를 찾으려면 이 파일id이 필요합니다.

    이제 Book Constructor를 사용하여 새 책을 만들 수 있습니다. DOM를 사용하여 .addEventListener를 조작하여 입력 항목을 캡처하고 새 책을 만들 수 있습니다.

    이벤트 리스너: 새 책 추가



    document.querySelector('form').addEventListener('submit', e => {
        e.preventDefault();
    
        //Create a new book
        const title = document.querySelector('#bookTitle').value;
        const author = document.querySelector('#bookAuthor').value;
        const pages = document.querySelector('#bookPages').value;
        const status = document.querySelector('input[name="readStatus"]:checked').value;
    
        const book = new Book(title, author, pages, status);
    
        //Add new book to library
        addBookToLibrary(book);
    }
    

    이제 이 책을 myLibrary 배열에 추가하고 배열을 반복하여 렌더링해야 합니다.

    어레이 푸시: 라이브러리에 책 추가



    function addBookToLibrary(book) {
        myLibrary.push(book);
    
        //What to do after I push the new book to the library
        displayBooks();
        clearFields();
    }
    

    함수displayBooks는 각 책에 대한 정보로 테이블의 행을 렌더링합니다. 그리고 함수clearFields()는 화면에서 내 입력 값을 지울 것입니다.

    렌더링 테이블 행



    function displayBooks() {
      const tBody = document.querySelector('#bookRow');
      tbody.querySelectorAll('tr').forEach(el => el.remove());
    
      myLibrary.forEach(book => {
        createRow(book)
      });
    }
    
    function clearFields() {
        document.querySelector('#bookTitle').value = '';
        document.querySelector('#bookAuthor').value = '';
        document.querySelector('#bookPages').value = '';
    }
    

    이 코드 줄에 대해 이야기해야 합니다. tbody.querySelectorAll('tr').forEach(el => el.remove());
    이 코드를 작성하면서 첫 번째, 두 번째, 세 번째에 어려움을 겪었습니다.
    테이블을 다시 렌더링할 때마다 테이블이 복제되었습니다.
    이것이 제가 찾은 기능적 솔루션입니다.
  • tr에서 <tbody>를 모두 제거합니다.
  • 모든 것을 다시 생성

  • 이것이 최선의 솔루션인지는 모르겠지만 저와 이 프로젝트에 효과적이었습니다.

    테이블 행 만들기



    function createRow(book) {
        const tbody = document.querySelector('#bookRow');
        const row = document.createElement('tr');
    
        if (book.status === 'true') {
            row.innerHTML = `
            <td>${book.title}</td>
            <td>${book.author}</td>
            <td>${book.pages}</td>
            <td>Read</td>
            <td id="delete">Delete</td>
        `
        } else {
            row.innerHTML = `
            <td>${book.title}</td>
            <td>${book.author}</td>
            <td>${book.pages}</td>
            <td>Not Read</td>
            <td id="delete">Delete</td>
        `
        }
    
    
    
        row.classList.add(`${book.id}`)
        tbody.appendChild(row);
    }
    

    if-else 조건은 설명적입니다.
    각 책의 현재 상태를 확인하고 boolean expression 를 평가합니다. 입력이 문자열을 반환하기 때문에 문자열과 비교됩니다.
    또한 Book Constructor에서 생성된 고유 ID를 사용하여 각각에 대한 클래스 이름<tr>을 추가합니다.

    이벤트 리스너: 책 삭제 및 상태 변경



    
    document.querySelector('table').addEventListener('click', e => {
        if (e.target.textContent === 'Delete') {
            const id = e.target.parentElement.className;
            deleteBook(id);
        }
    
        if (e.target.textContent === 'Read' || e.target.textContent === 'Not Read') {
            const id = e.target.parentElement.className;
            changeStatus(id);
        }
    
    })
    

    여기에서 실제로 내 table와 상호 작용할 수 있습니다. string values로 렌더링하고 있으므로 실행하려는 기능을 트리거하기 위해 정확히 .textContent를 찾고 있습니다.

    function deleteBook(id) {
        myLibrary.forEach((book, i) => {
            if (book.id == id) {
                myLibrary.splice(i, 1);
            }
        })
    
        displayBooks();
    }
    
    function changeStatus(id) {
        myLibrary.forEach((book, i) => {
            if (book.id == id) {
                if (book.status === 'true') {
                    book.status = 'false'
                } else {
                    book.status = 'true'
                }
            }
        })
    
        displayBooks();
    }
    


    각 변경 후에는 displayBooks()를 다시 호출합니다. 내 테이블을 다시 렌더링하고(내 tbody 내부의 모든 항목 제거) 새 배열m̀yLibrary 또는 업데이트된 배열length을 사용하여 새 배열book.status을 렌더링합니다.

    그게 다야.

    나는 당신이 이 튜토리얼과 내가 이 도전을 어떻게 해결했는지 즐겼기를 바랍니다!

    내에서 나를 따르라!

    좋은 웹페이지 즐겨찾기