Search code examples
javascripthtmlevent-handling

Adding a JS Button Click Handler function with chaining javascript functions within it for a Book Finder program


I basically have a help function file (helper.js) that is used to test my functions that are implemented in script.js. After the project is rendered the user should be able to search for a book that is provided in a list of books (bookList.js) and when found the book is found it is displayed in in this HTML (index.html) file loaded to the browser. The issue I am having is implementing a searchBtnClickHandler function in the script.js file. I know there can be anonymous click handlers but how do I have a defined click handler function (defined as searchBtnClickHandler in script.js) connected to the search button and chain the previous functions (captureSearchValue, filterBooks and structureBooksAsHtml) in it? I hope the instructions in the comments provided with each task provides more reference of what each part of the program is intended to do. If not please comment so I can edit it to add more clarification here. Thank you!

script.js

// Click handler for search button
const captureSearchValue = () => {
  const input = document.getElementById("search-bar").value;
  return input;
};

// Filter books based on search input and render filtered books to the DOM
const filterBooks = (input, books) => {
  const filteredBooks = [];
  let flattenedBooks = flattenObjectValuesIntoArray(books); // Call existing function
  for (let book = 0; book < books.length; book++) {
    // Search in the flattened books array, also make lowercase to include case insensitive results
    if (
      flattenedBooks[book]
        .map((e) => e.toLowerCase())
        .includes(input.toLowerCase())
    ) {
      filteredBooks.push(books[book]);
    }
  }
  return filteredBooks;

  // const filteredBooks = books.filter(book => book.tags.includes(input));
  // const filteredBooks = books.filter(book => book.tags.includes(input));
  // books.filter((book) => book.tags.includes(input));
};

// Empty the book list container, iterate over list of filtered books, return list of books formatted as HTML using the function in `helper.js`
const structureBooksAsHtml = (filteredBooks) => {
  const bookHTML = [];
  for (let book = 0; book < filteredBooks.length; book++) {
    bookHTML.push(structureBookAsHtml(filteredBooks[book]));
  }
  return bookHTML;
};

// Handler triggered when a user clickers the "Search" button. Chains previously defined functions together to filter books based on the search value, formats the books as HTML and renders them to the DOM
const searchBtnClickHandler = (event) => {
  captureSearchValue();
  filterBooks();
  structureBooksAsHtml();
};

// Grab search button from the DOM
searchBtn = document.getElementById("search-btn");
// Attach an event listener to the search button
searchBtn.addEventListener("click", () => {
  searchBtnClickHandler(books);
});

helper.js

/ Flatten object keys into an array so that we search the entire object by the input value
const flattenObjectValuesIntoArray = (arrOfObjs) => {
  let flattenedObj;
  const flattenedObjsArr = [];
  for (let obj = 0; obj < arrOfObjs.length; obj++) {
    const objValues = Object.values(arrOfObjs[obj]);
    flattenedObj = [...objValues.flat()]
    flattenedObjsArr.push(flattenedObj)
  }
  return flattenedObjsArr;
};

// Structure filtered books as HTML and return
const structureBookAsHtml = (book) => {
  const bookDiv = document.createElement("div");
  bookDiv.setAttribute('class', 'bookDiv');
  
  const bookTitle = document.createElement("h2");
  bookTitle.innerHTML = book.title;
  bookTitle.setAttribute('class', 'bookTitle');

  const bookAuthor = document.createElement("h3");
  bookAuthor.innerHTML = book.author;

  const bookTags = document.createElement("p");
  bookTags.innerHTML = book.tags.join(", ");

  bookDiv.append(bookTitle, bookAuthor, bookTags);
  
  return bookDiv;
};

const renderBooksToDom = (elements) => {
  const bookListContainer = document.querySelector("#bookList");
  bookListContainer.innerHTML = "";

  bookListContainer.append(...elements);
};

bookList.js

const books = [
  {
    title: "The City We Became",
    author: "N. K. Jemisin",
    tags: ["fantasy", "fiction", "afrofutursim", "science fiction", "sci-fi"]
  },
  {
    title: "The Catcher in the Rye",
    author: "J. D. Salinger",
    tags: ["fiction", "young adult", "YA", "realism", "coming of age", "classic"]
  },
  {
    title: "The Hundred Thousand Kingdoms",
    author: "N. K. Jemisin",
    tags: ["fantasy", "fiction", "adventure", "series"]
  },
  {
    title: "Sapiens: A Brief History of Humankind",
    author: "Yuval Noah Harari",
    tags: ["nonfiction", "history", "anthropology", "science", "sociology"]
  },
  {
    title: "Behave: The Biology of Humans at Our Best and Worst",
    author: "Robert M. Sapolsky",
    tags: ["nonfiction", "anthropology", "science", "sociology", "biology"]
  },
  {
    title: "The Parable of the Talents",
    author: "Octavia Butler", 
    tags: ["fiction", "dystopian", "science fiction"]
  },
  {
    title: "1984",
    author: "George Orwell", 
    tags: ["fiction", "dystopian", "science fiction", "classics", "adult"]
  },
  {
    title: "Remarkably Bright Creatures",
    author: "Shelby Van Pelt",
    tags: ["fiction", "mystery", "magical realism"]
  },
  {
    title: "Crying in H Mart",
    author: "Michelle Zauner",
    tags: ["memoir", "nonfiction", "autobiography"]
  },
  {
    title: "Wild: From Lost to Found on the Pacific Crest Trail",
    author: "Cheryl Strayed",
    tags: ["nonfiction", "memoir", "adventure", "travel"]
  }
]

index.html

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="styles.css">
    <script src="bookList.js"></script>
    <script src="bookList.test.js"></script>
    <script src="helper.js"></script>
    <script src="script.js" defer></script>
  </head>
  <body>
    <h1>Book Finder</h1>
    <input id="search-bar" type="text" placeholder="Search for books by tags">
    <button class="btn" id="search-btn">Search</button>
    <div id="bookList">
      <!-- List of books will be rendered here -->
    </div>
  </body>
</html>
const searchBtnClickHandler = (event) => {
  captureSearchValue();
  filterBooks();
  structureBooksAsHtml();
};


In this line of the code I am trying to chain the functions to the event. I think this is where I am falling short at but I am open to any suggestions on if the error lies in this part of code or somewhere else.


Solution

  • Functions like captureSearchValue() are ordinary functions, not methods of the books array. You should call them with arguments, not as event.functionName(). Then capture the return value in another variable so you can pass it to the next function in the chain.

    // Handler triggered when a user clickers the "Search" button. Chains previously defined functions together to filter books based on the search value, formats the books as HTML and renders them to the DOM
    const searchBtnClickHandler = (books) => {
      let input = captureSearchValue();
      let filtered = filterBooks(input, books);
      let array_of_divs = structureBooksAsHtml(filtered);
      renderBooksToDom(array_of_divs);
    };