diff options
| author | Arne Rief <riearn@proton.me> | 2025-12-22 14:02:07 +0100 |
|---|---|---|
| committer | Arne Rief <riearn@proton.me> | 2025-12-22 14:02:07 +0100 |
| commit | 038054b8206a9c25e84adeb0f0f355abd22d6143 (patch) | |
| tree | 47700e52b88e0db08f0ca113de22f4c0d05cc405 /assets/js | |
| parent | 32c1a5dd203435e8bef324306d1516e28ce14615 (diff) | |
CSS & JS splitting, small fix section badge
Diffstat (limited to 'assets/js')
| -rw-r--r-- | assets/js/main.js | 151 | ||||
| -rw-r--r-- | assets/js/search.js | 118 | ||||
| -rw-r--r-- | assets/js/theme.js | 32 |
3 files changed, 153 insertions, 148 deletions
diff --git a/assets/js/main.js b/assets/js/main.js index d849b4e..957c9b5 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -1,152 +1,7 @@ -(function () { - "use strict"; - - // TOGGLE DARK/LIGHT MODE - function initThemeToggle() { - const rootHtml = document.documentElement; - const toggleThemeBtn = document.getElementById("theme-toggle"); - - // If no saved theme, determine user preference, otherwise default to light - const savedTheme = localStorage.getItem("theme"); - const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; - const initialTheme = savedTheme ?? (prefersDark ? "dark" : "light"); - - function setTheme(theme) { - const isDarkMode = theme === "dark"; - // toggleThemeBtn dataset comes with translated labels for site's language - const label = isDarkMode ? toggleThemeBtn.dataset.labelLight : toggleThemeBtn.dataset.labelDark; - - rootHtml.setAttribute("data-theme", theme); - toggleThemeBtn.setAttribute("aria-label", label); // display handled by CSS - } - - // Apply initial theme - setTheme(initialTheme); - - // Change theme on click and save user's choice - toggleThemeBtn.addEventListener("click", () => { - const newTheme = rootHtml.getAttribute("data-theme") === "dark" ? "light" : "dark"; - setTheme(newTheme); - localStorage.setItem("theme", newTheme); - }); - } - - // 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; - } - - function renderSearchResults(matches) { - clearResults(); - - // Display how many results found for query - resultsCount.hidden = false; - resultsCount.querySelector("#search-results-number").textContent = String(matches.length ?? 0); - - // No posts matching query found - if (!matches.length) return; - - // Hydrate post-card list item(s) from the template with JSON data - matches.forEach((post) => { - const li = template.content.firstElementChild.cloneNode(true); - - const link = li.querySelector(".post-card__link"); - link.href = post.url; - link.innerHTML = post.title; - - const section = li.querySelector(".post-card__section-badge"); - if (section) section.textContent = post.section; - - const date = li.querySelector(".post-card__publish-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(); - } +import initThemeToggle from "./theme.js"; +import initSearch from "./search.js"; +(function () { initThemeToggle(); initSearch(); })(); diff --git a/assets/js/search.js b/assets/js/search.js new file mode 100644 index 0000000..f10695a --- /dev/null +++ b/assets/js/search.js @@ -0,0 +1,118 @@ +/* Search Entire Site for Content */ +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; + } + + function renderSearchResults(matches) { + clearResults(); + + // Display how many results found for query + resultsCount.hidden = false; + resultsCount.querySelector("#search-results-number").textContent = String(matches.length ?? 0); + + // No posts matching query found + if (!matches.length) return; + + // Hydrate post-card list item(s) from the template with JSON data + matches.forEach((post) => { + const li = template.content.firstElementChild.cloneNode(true); + + const link = li.querySelector(".post-card__link"); + link.href = post.url; + link.innerHTML = post.title; + + const section = li.querySelector(".post-card__section-badge"); + if (section) section.textContent = post.section; + + const date = li.querySelector(".post-card__publish-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 matches with 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) => { + clearTimeout(searchTimeout); + // Debounce search + 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(); +} + +export default initSearch; + diff --git a/assets/js/theme.js b/assets/js/theme.js new file mode 100644 index 0000000..36896c9 --- /dev/null +++ b/assets/js/theme.js @@ -0,0 +1,32 @@ +/* Toggle Dark/Light Mode */ +function initThemeToggle() { + const rootHtml = document.documentElement; + const toggleThemeBtn = document.getElementById("theme-toggle"); + + // If no saved theme, determine user preference, otherwise default to light + const savedTheme = localStorage.getItem("theme"); + const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; + const initialTheme = savedTheme ?? (prefersDark ? "dark" : "light"); + + function setTheme(theme) { + const isDarkMode = theme === "dark"; + // toggleThemeBtn dataset comes with translated labels for site's language + const label = isDarkMode ? toggleThemeBtn.dataset.labelLight : toggleThemeBtn.dataset.labelDark; + + rootHtml.setAttribute("data-theme", theme); + toggleThemeBtn.setAttribute("aria-label", label); // display handled by CSS + } + + // Apply initial theme + setTheme(initialTheme); + + // Change theme on click and save user's choice + toggleThemeBtn.addEventListener("click", () => { + const newTheme = rootHtml.getAttribute("data-theme") === "dark" ? "light" : "dark"; + setTheme(newTheme); + localStorage.setItem("theme", newTheme); + }); +} + +export default initThemeToggle; + |
