-
Notifications
You must be signed in to change notification settings - Fork 3.8k
add search functionality #3174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add search functionality #3174
Changes from 11 commits
f07db6a
ebdfefa
d70293b
f001a42
2d6029d
882649b
15f84d1
14d1d0b
0458594
d61b742
5adf3c8
e8a2447
4e6e82e
ba3e9ba
d16f376
544e0e6
f5a7835
33dc4c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
dragon-slayer27 marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,107 +1,125 @@ | ||
| // Load the cards.json file using Fetch API | ||
| fetch('./public/cards.json') | ||
| .then((response) => { | ||
| if (!response.ok) { | ||
| throw new Error('Network response was not ok'); | ||
| } | ||
| return response.json(); // Parse the JSON file content | ||
| }) | ||
| .then((cards) => { | ||
| /* Shuffles cards' order */ | ||
| function shuffle(o) { | ||
| for ( | ||
| let j, x, i = o.length; | ||
| i; | ||
| j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x | ||
| ); | ||
| return o; | ||
| } | ||
|
|
||
| /** Creates cards from the array above */ | ||
| const getCardContents = (cardList) => { | ||
| return shuffle(cardList).map((c) => [ | ||
| `<li class="card">` + | ||
| `<a href='${c.pageLink}'>` + | ||
| `<img class="art-image" src='${c.imageLink}' alt='${c.artName}' />` + | ||
| `</a>` + | ||
| `<a class="art-title" href='${c.pageLink}'><h3 >${c.artName}</h3></a>` + | ||
| `<p class='author'><a href="${c.githubLink}" target="_blank"><i class="fab fa-github"></i> ${c.author}</a> </p>` + | ||
| `</li>` | ||
| ]); | ||
| }; | ||
|
|
||
| /* Injects cards list HTML into the DOM */ | ||
| let contents = getCardContents(cards); | ||
| document.getElementById('cards').innerHTML = contents; | ||
|
|
||
| /* Adds scroll to top arrow button */ | ||
| window.onscroll = function () { | ||
| if (window.scrollY > 100) { | ||
| goToTopBtn.classList.add('active'); | ||
| } else { | ||
| goToTopBtn.classList.remove('active'); | ||
| } | ||
| }; | ||
|
|
||
| // Adds the click event to the button | ||
| const goToTopBtn = document.querySelector('.go-to-top'); | ||
| goToTopBtn.addEventListener('click', function () { | ||
| window.scrollTo({ | ||
| top: 0, | ||
| behavior: 'smooth' | ||
| }); | ||
| }); | ||
|
|
||
| // Get element by id "stats" and set the innerHTML to the following | ||
| document.getElementById( | ||
| 'stats' | ||
| ).innerHTML = `Showcasing ${cards.length} artworks`; | ||
| }) | ||
| .catch((error) => { | ||
| console.error('Error fetching the cards.json file:', error); | ||
| }); | ||
| // Shuffles an array's elements into a random order without mutating the original. | ||
| function shuffle(array) { | ||
| const newArray = [...array]; | ||
| for ( | ||
| let j, x, i = newArray.length; | ||
| i; | ||
| j = parseInt(Math.random() * i), | ||
| (x = newArray[--i]), | ||
| (newArray[i] = newArray[j]), | ||
| (newArray[j] = x) | ||
| ); | ||
| return newArray; | ||
| } | ||
|
|
||
| // 🎨 Hacktoberfest Card Data | ||
| const cardList = [ | ||
| { | ||
| artName: "HACKTOBERFEST", | ||
| pageLink: "index.html", | ||
| imageLink: "hacktoberfest-logo.png", | ||
| author: "Takunda", | ||
| githubLink: "https://github.com/Enock12234" | ||
| } | ||
| ]; | ||
| // Returns a function that delays invoking its callback until after a specified delay. | ||
| function debounce(func, delay = 300) { | ||
| let timer; | ||
| return (...args) => { | ||
| clearTimeout(timer); | ||
| timer = setTimeout(() => { | ||
| func.apply(this, args); | ||
| }, delay); | ||
| }; | ||
| } | ||
|
|
||
| // 🔀 Optional shuffle function (add if not defined) | ||
| function shuffle(array) { | ||
| return array.sort(() => 0.5 - Math.random()); | ||
| // Renders a generic message ("No results") into a specified container. | ||
| function renderMessage(container, message) { | ||
| if (!container) return; | ||
| container.innerHTML = `<p class="no-results">${message}</p>`; | ||
| } | ||
|
||
|
|
||
| // 🖼️ Generate HTML cards | ||
| const getCardContents = (cardList) => { | ||
| return shuffle(cardList) | ||
| .map((c) => ` | ||
| // Renders a list of card objects into the main card container. | ||
| function renderCards(container, cardList) { | ||
| if (!container) return; | ||
| if (cardList.length === 0) { | ||
| renderMessage(container, "No artworks found."); | ||
| return; | ||
| } | ||
| const html = cardList | ||
| .map((card) => ` | ||
| <li class="card"> | ||
| <a href='${c.pageLink}'> | ||
| <img class="art-image" src='${c.imageLink}' alt='${c.artName}' /> | ||
| <a href='${card.pageLink || "#"}'> | ||
| <img class="art-image" src='${card.imageLink || ""}' alt='${card.artName || "Untitled"}' /> | ||
| </a> | ||
| <a class="art-title" href='${c.pageLink}'> | ||
| <h3>${c.artName}</h3> | ||
| <a class="art-title" href='${card.pageLink || "#"}'> | ||
| <h3>${card.artName || "Untitled"}</h3> | ||
| </a> | ||
| <p class='author'> | ||
| <a href="${c.githubLink}" target="_blank"> | ||
| <i class="fab fa-github"></i> ${c.author} | ||
| <a href="${card.githubLink || "#"}" target="_blank"> | ||
| <i class="fab fa-github"></i> ${card.author || "Unknown"} | ||
| </a> | ||
| </p> | ||
| </li> | ||
| `) | ||
| .join(''); | ||
| }; | ||
| .join(""); | ||
| container.innerHTML = html; | ||
| } | ||
|
|
||
| // Filters cards, updates stats, and triggers re-renders based on search input. | ||
| function handleSearch(elements, masterCardList) { | ||
| const { searchInput, cardsContainer, statsElement, clearBtn } = elements; | ||
| const query = searchInput.value.toLowerCase().trim(); | ||
|
|
||
| // 🧩 Inject into the DOM | ||
| document.addEventListener('DOMContentLoaded', () => { | ||
| const container = document.getElementById("cardContainer"); | ||
| if (container) { | ||
| container.innerHTML = getCardContents(cardList); | ||
| clearBtn.classList.toggle("visible", query.length > 0); | ||
|
|
||
| if (query === "") { | ||
| statsElement.innerHTML = `Showcasing ${masterCardList.length} artworks`; | ||
| renderCards(cardsContainer, shuffle(masterCardList)); | ||
| } else { | ||
| const filteredList = masterCardList.filter((card) => { | ||
| if (!card) return false; | ||
| const artName = (card.artName || "").toLowerCase(); | ||
| const author = (card.author || "").toLowerCase(); | ||
| return artName.includes(query) || author.includes(query); | ||
| }); | ||
| statsElement.innerHTML = `Showcasing ${masterCardList.length} artworks | ${filteredList.length} found`; | ||
|
||
| renderCards(cardsContainer, filteredList); | ||
| } | ||
| } | ||
|
|
||
| // Fetches initial data and sets up all application event listeners. | ||
| async function initApp() { | ||
| const elements = { | ||
| searchInput: document.getElementById("search-input"), | ||
| cardsContainer: document.getElementById("cards"), | ||
| statsElement: document.getElementById("stats"), | ||
| goToTopBtn: document.querySelector(".go-to-top"), | ||
| clearBtn: document.getElementById("clear-btn"), | ||
| }; | ||
|
|
||
| try { | ||
| const response = await fetch("./public/cards.json"); | ||
| if (!response.ok) throw new Error("Failed to fetch cards.json"); | ||
| const data = await response.json(); | ||
|
|
||
| const masterCardList = data; | ||
|
|
||
| const debouncedSearch = debounce(() => handleSearch(elements, masterCardList), 300); | ||
| elements.searchInput.addEventListener("input", debouncedSearch); | ||
|
|
||
| elements.clearBtn.addEventListener("click", () => { | ||
| elements.searchInput.value = ""; | ||
| handleSearch(elements, masterCardList); | ||
| elements.searchInput.blur(); | ||
| }); | ||
|
|
||
| // Manages the initial render, including handling the browser back button state. | ||
| handleSearch(elements, masterCardList); | ||
|
|
||
| // Sets up the go-to-top button functionality. | ||
| window.onscroll = () => { | ||
| elements.goToTopBtn.classList.toggle("active", window.scrollY > 100); | ||
| }; | ||
| elements.goToTopBtn.addEventListener("click", () => { | ||
| window.scrollTo({ top: 0, behavior: "smooth" }); | ||
| }); | ||
|
|
||
| } catch (error) { | ||
| console.error("Error initializing app:", error); | ||
| renderMessage(elements.cardsContainer, "Error: Could not load artworks."); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| // Kicks off the application once the DOM is fully loaded. | ||
| document.addEventListener("DOMContentLoaded", initApp); | ||
dragon-slayer27 marked this conversation as resolved.
Show resolved
Hide resolved
|
Uh oh!
There was an error while loading. Please reload this page.