diff options
Diffstat (limited to 'assets/js/main.js')
| -rw-r--r-- | assets/js/main.js | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/assets/js/main.js b/assets/js/main.js index c3218f1..08cfcf5 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -33,6 +33,119 @@ }); } + // SEARCH + function initSearch() { + const input = document.getElementById("search-input"); + const resetBtn = document.getElementById("search-reset"); + const resultsCount = document.querySelector(".search-results__count"); + const resultsList = document.querySelector(".search-results__list"); + const template = document.getElementById("search-result-template"); + + // Only initialize search on the search page + if (!input || !template) return; + + let allPosts = []; + let searchTimeout; + + // Get JSON file with data of all posts + fetch(input.dataset.indexUrl ?? "/index.json") + .then((res) => res.json()) + .then((data) => allPosts = data) + .catch((err) => console.error("Search index.json failed to load", err)); + + function clearResults() { + resultsCount.hidden = true; + resultsList.innerHTML = ""; + resultsList.hidden = true; + } + + // Hydrate post-card list item(s) from the template with JSON data + function renderSearchResults(matches) { + clearResults(); + + resultsCount.hidden = false; + resultsCount.querySelector("#search-results-number").textContent = String(matches.length ?? 0); + + // No posts matching query found + if (!matches.length) return; + + matches.forEach((post) => { + const li = template.content.firstElementChild.cloneNode(true); + + const link = li.querySelector(".post-card__link"); + link.href = post.url; + link.tile = post.title; + + const date = li.querySelector(".post-card__date"); + date.textContent = new Date(post.date).toLocaleDateString(); + date.setAttribute("datetime", post.date); + + const summary = li.querySelector(".post-card__summary"); + if (summary) summary.textContent = post.summary; + + const tagsList = li.querySelector(".post-card__tags-list"); + if (tagsList) { + tagsList.innerHTML = ""; + if (post.tags && post.tags.length) { + post.tags.slice(0, 3).forEach((tag) => { + const liTag = document.createElement("li"); + liTag.className = "post-card__tags-item"; + liTag.textContent = `#${tag}`; + tagsList.appendChild(liTag); + }); + + if (post.tags.length > 3) { + const more = document.createElement("li"); + more.className = "post-card__tags-item post-card__tags-more"; + more.textContent = `+${post.tags.length - 3}`; + tagsList.appendChild(more); + } + } + } + + resultsList.appendChild(li); + resultsList.hidden = false; + }); + } + + // Filter all posts for ones matching the user's search query + function searchPosts(query) { + const normalizedQuery = query.trim().toLowerCase(); + + // Only search if user entered at least 3 chars + if (normalizedQuery.length < 3) { + clearResults(); + return; + } + + const matches = allPosts.filter((post) => ( + post.title.toLowerCase().includes(normalizedQuery) || + (post.summary && post.summary.toLowerCase().includes(normalizedQuery)) + )); + + // At least 1 post matching query found + renderSearchResults(matches); + } + + input.addEventListener("input", (event) => { + // Debounce search + clearTimeout(searchTimeout); + searchTimeout = setTimeout(() => { + searchPosts(event.target.value); + }, 300); + }); + + resetBtn.addEventListener("click", () => { + input.value = ""; + input.focus(); + clearResults(); + }); + + // Focus input on page load - it's what the user is here for + input.focus(); + } + initThemeToggle(); + initSearch(); })(); |
