<!DOCTYPE html> <html lang="en"> <head> <title>Naomi's Book Library</title> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="description" content="An interactive explorer for the books Naomi reads." /> <script src="https://cdn.nhcarrigan.com/headers/index.js" async defer></script> </head> <body> <main> <h1>Naomi's Book Library</h1> <section> <p>An interactive explorer for the books Naomi reads.</p> <p id="count">Loading library...</p> </section> <div style="display: none;"> <span>Search Authors: </span> <input type="text" id="author" /> </div> <div style="display: none;"> <span>Search Titles: </span> <input type="text" id="title" /> </div> <div style="display: none;"> <button type="button" id="clear">Clear Filters</button> </div> <table id="books"> </table> </main> </body> <script> const authorQuery = document.getElementById('author'); const titleQuery = document.getElementById('title'); const resetButton = document.getElementById('clear'); const bookTable = document.getElementById('books'); const filterBooks = (author, title) => { let result = [...bookList]; if(author) { result = result.filter(book => book.author.toLowerCase().includes(author.toLowerCase())); } if(title) { result = result.filter(book => book.title.toLowerCase().includes(title.toLowerCase())); } resetButton.parentElement.style.display = author || title ? "block" : "none"; document.getElementById('count').innerText = author || title ? `Filtered to ${result.length} books from ${bookList.length}.` : `Naomi currently has ${bookList.length} books.`; updateTable(result); } const loadBooks = (books) => { bookList.push(...books); authorQuery.value = ""; titleQuery.value = ""; authorQuery.parentElement.style.display = "block"; titleQuery.parentElement.style.display = "block"; document.getElementById('count').innerText = `Naomi currently has ${books.length} books.`; updateTable(books); } const updateTable = (books) => { books = books.sort((a, b) => a.title.localeCompare(b.title)); bookTable.innerHTML = ""; const header = document.createElement('tr'); const authorHeader = document.createElement('th'); authorHeader.innerText = "Author"; const titleHeader = document.createElement('th'); titleHeader.innerText = "Title"; header.appendChild(titleHeader); header.appendChild(authorHeader); bookTable.appendChild(header); books.forEach(book => { const row = document.createElement('tr'); const author = document.createElement('td'); author.innerText = book.author; const title = document.createElement('td'); title.innerText = book.title; row.appendChild(title); row.appendChild(author); bookTable.appendChild(row); }); } const bookList = []; fetch("./books.json").then(res => res.json()).then(data => loadBooks(data)) authorQuery?.addEventListener("input", (e) => filterBooks(e.target.value, titleQuery.value)); titleQuery?.addEventListener("input", (e) => filterBooks(authorQuery.value, e.target.value)); resetButton?.addEventListener("click", () => { authorQuery.value = ""; titleQuery.value = ""; filterBooks("", ""); }); </script> <style> table { width: 100%; border-collapse: collapse; } tr:nth-of-type(even) { background-color: #db7093dd; color: #ffefef; } input { background:var(--foreground); color:var(--background); border:1px solid white; border-radius:10px; padding:.25rem } button { background:var(--foreground); color:var(--background); border:1px solid white; border-radius:10px; padding:.25rem; cursor:url('https://cdn.nhcarrigan.com/cursors/pointer.cur'), pointer; } </style> </html>