Search code examples
javascriptdomtogglebutton

Javascript - Create Toggle function for DOM created elements


I have a button "-" that when on click, it creates an "X" button on the corner of my "books" to delete them. Is there a way to make it so that when I click the "-" button again it toggles between hiding and displaying the "X" button in the corner of the "books"?

No JQuery please, still have not learned how to use it

here is my HTML:

<div class="container">
<div class="book-display">
  <div class="title">Title:</div>
  <div class="author">Author:</div>
  <div class="pages">Pages:</div>
  <div class="read">Have Read:</div>
</div>
<div class="shelf"></div>
<div class="buttons">
  <button class="button" id="delete">➖</button>
  <button class="button" id="add">➕</button>
</div>

and here is my Javascript:

 //Add Books to Shelf Display
function addBookToShelf() {
    for (let i = 0; i < myLibrary.length; i++) {
        const books = document.createElement('div');
        books.classList.add('books');
        books.setAttribute('id', myLibrary[i].title)
        if (myLibrary[i].read === "Not Read") {
            books.style.background = "rgba(71, 22, 10, 0.664)"
        };
        if (myLibrary[i].read === "Partially Read") {
            books.style.background = "rgba(199, 199, 58, 0.575)"
        }
        const booksText = document.createElement('div');
        booksText.classList.add('book-text')
        booksText.addEventListener('click', () => {
            bookTitleEl.innerHTML = "Title: " + myLibrary[i].title;
            bookAuthorEl.innerHTML = "Author: " + myLibrary[i].author;
            bookPagesEl.innerHTML = "Pages: " + myLibrary[i].pages;
            bookReadEl.innerHTML = "Have Read: " + myLibrary[i].read;
        });
        booksText.innerHTML = myLibrary[i].title;
        shelfEl.appendChild(books);
        books.appendChild(booksText);
        document.getElementById(myLibrary[i].title).setAttribute('value', 1);

        

//Add delete button to books function
function deleteXButton() {
    const deleteXEl = document.createElement('button');
    deleteXEl.classList.add('deleteX');
    books.appendChild(deleteXEl);
    
    deleteXEl.innerHTML = "X"
    document.getElementsByClassName('deleteX');
    if (document.getElementsByClassName('deleteX').length > myLibrary.length) {
    deleteXEl.remove();
    }
    if (deleteXEl.style.display === "block") {
        deleteXEl.style.display = "none";
      } else {
        deleteXEl.style.display = "block";
      }
    //Delete Book from Shelf & Object from Libray Array
    deleteXEl.addEventListener('click', () => {
        const bookIndex = myLibrary.indexOf(myLibrary[i])
        books.remove(delete myLibrary[bookIndex]);
        noBookFound();
    });
}

        //DELETE BUTTON EVENT LISTENER   
         deleteButtonEl.addEventListener('click', () => {
                deleteXButton();
        });
        }
      };

Any and all help would be greatly appreciate it!


Solution

  • The classList API has a toggle method that we can use to toggle styles. If you include a class that removes elements from the DOM (as with display: none), this becomes an easy way to hide/show your elements in response to a user action. You can see how this works in the toggleDeleteButtons function in the snippet.

    I made a bunch of other suggested modifications to your code, which you are free to take or leave on an à la carte basis. Btw, the link above goes to MDN, which is a great site to search for any unfamiliar web-dev term (e.g, you'd just google "MDN findIndex" to learn about the findIndex method of Arrays.)

    // Identifies some DOM elements globally
    const
      deleteBooksButton = document.getElementById("delete"),
      addBookButton = document.getElementById("add"),
      shelfEl = document.getElementsByClassName("shelf")[0],
      deleteButtons = document.getElementsByClassName("delete-button");
    
    // Declares the library globally, and populates it with sample books
    let library;
    library = getSampleLibrary(library);
    addBooksToShelf(library);
    
    // Calls toggleDeleteButtons when deleteBooksButton is clicked
    deleteBooksButton.addEventListener("click", toggleDeleteButtons);
    
    // Calls deleteBook when anything inside shelf is clicked
    shelfEl.addEventListener("click", deleteBook);
    
    
    // Defines function to show/hide all delete buttons
    function toggleDeleteButtons(){
      for(let button of deleteButtons){
        button.classList.toggle("hidden");
      }
    }
    
    
    // Defines function to delete a book (Click events bubble up to `shelf`)
    function deleteBook(event){
      // Makes sure the click event was on a delete-button before proceeding
      const clickedThing = event.target;
      if(!clickedThing.classList.contains("delete-button")){ return; }
    
      // Searches upward in DOM tree for bookEl, then downward for title
      const
        bookEl = clickedThing.closest(".book"),
        title = bookEl.querySelector(".title").textContent;
    
      // Removes bookEl from the DOM tree
      bookEl.remove();
    
      // Looks in library array for book object with matching title property
      const libraryIndex = library.findIndex(book => book.title == title);
    
      // Removes book object from library if there was a match
      if(libraryIndex > -1){
        library.splice(libraryIndex, 1);
      }
    }
    
    
    // Defines function to populate shelf's DOM tree with book-related elements
    function addBooksToShelf(library) {
    
      // Loops through elements of library array (referring to each as `book`)
      for (let book of library) {
    
        // Uses "destructuring" to get local variables bound to props of book
        const { title, author, pages, read } = book;
    
        // Creates bookEl and its descendants
          // bookEl will have 3 children: deleteDiv, teaserEl, and detailsEl
          // deleteDiv will have 1 child: deleteButton
          // detailsEl will have 4 div children (title, author, pages, and read)
          //   (The 4 divs inside detailsEl will each have 1 span child)
        const
          bookEl = document.createElement("div"), // Will have class: "book"
          deleteDiv = document.createElement("div"), // ... "delete-div"
          teaserEl = document.createElement("div"), // ... "teaser" 
          detailsEl = document.createElement("div"), // ... "text"
    
          titleDiv = document.createElement("div"),
          authorDiv = document.createElement("div"),
          pagesDiv = document.createElement("div"),
          readDiv  = document.createElement("div"),
    
          titleSpan = document.createElement("span"), // ... "title" 
          authorSpan = document.createElement("span"), // ... "author"
          pagesSpan = document.createElement("span"), // ... "pages"
          readSpan  = document.createElement("span"); // ... "read"
    
        // Creates and configures deleteButton, and appends it to deleteDiv
        deleteButton = document.createElement("button");
        deleteButton.classList.add("delete-button");
        deleteButton.classList.add("hidden");
        deleteButton.textContent = "X";
        deleteDiv.appendChild(deleteButton);
    
        // Configures deleteDiv, and appends it to bookEl
        deleteDiv.classList.add("delete-div");
        bookEl.appendChild(deleteDiv);
    
        // Configures teaserEl, and appends it to bookEl
        teaserEl.innerHTML = title;
        teaserEl.classList.add("teaser"); // teaser class
        bookEl.appendChild(teaserEl);
    
        // Configures the spans
        titleSpan.classList.add("title");
        authorSpan.classList.add("author");
        pagesSpan.classList.add("pages");
        readSpan.classList.add("read");
    
        titleSpan.textContent = title;
        authorSpan.textContent = author;
        pagesSpan.textContent = pages;
        readSpan.textContent = read;
    
        // Populates divs (w/ label text and spans), and adds them to detailsEl
        titleDiv.innerHTML = "Title: " + titleSpan.outerHTML;
        authorDiv.innerHTML = "Author: " + authorSpan.outerHTML
        pagesDiv.innerHTML = "Pages: " + pagesSpan.outerHTML
        readDiv.innerHTML = "Have Read: " + readSpan.outerHTML
    
        detailsEl.appendChild(titleDiv);
        detailsEl.appendChild(authorDiv);
        detailsEl.appendChild(pagesDiv);
        detailsEl.appendChild(readDiv);
    
        // Configures detailsEl, and appends it to bookEl
        detailsEl.classList.add('text'); // text class
        detailsEl.classList.add("hidden"); // detailsEl & children are hidden
        bookEl.appendChild(detailsEl);
    
        // Configures bookEl (w/ styles, listener, etc), and adds it to shelf
        const
          readBG = "rgba(22, 71, 10, 0.420)",
          notReadBG = "rgba(71, 22, 10, 0.664)",
          partReadBG = "rgba(199, 199, 58, 0.575)";
        if (read === "Partially Read") { bookEl.style.background = partReadBG; }
        else if (read === "Not Read") { bookEl.style.background = notReadBG; }
        else { bookEl.style.background = readBG; }
    
        bookEl.id = title; // (Careful: Book titles are not unique identifiers!)
        bookEl.classList.add("book");
        bookEl.setAttribute("data-value", 1); // (Custom attributes use "data-")
        bookEl.addEventListener('click', toggleText); // Listener on book
        shelfEl.appendChild(bookEl);
      }
    };
    
    
    // Defines function to show text and hide teaser (or vice versa)
    function toggleText(event){
    
      // Searches upward in DOM tree (from clicked element) to get closest book
      const bookEl = event.target.closest(".book");
    
      // Searches downward in DOM tree to find teaser & text, and update classes
      bookEl.querySelector(".teaser").classList.toggle("hidden");
      bookEl.querySelector(".text").classList.toggle("hidden");
    }
    
    
    // Defines function to return a sample library
    function getSampleLibrary(library){
      const sampleLibrary = [
        {
          title: "Harry Potter and the Methods of Rationality",
          author: "Yudkowsky, Eliezer",
          pages: 750,
          read: "Read"
        },
        {
          title: "Go, Dog. Go!",
          author: "Eastman, P.D.",
          pages: 16,
          read: "Partially Read"
        }
      ];
      library = sampleLibrary;
      return library;
    }
    .shelf{ width: 350px; }
    .book{ margin-top: 0.5em; padding: 0.5em; }
    .delete-div{ text-align: right; } /* divs exist only to align buttons */
    .delete-button{ border: 1px solid grey; border-radius: 0.3em; }
    .hidden{ display: none; }
    <div class="container">
      <div class="buttons">
        <button id="delete">Delete Books ➖</button>
        <button class="button" id="add">Add Book ➕</button>
      </div>
      <div class="shelf"></div>
    </div>