mirror of
https://github.com/thiloho/thiloho.github.io.git
synced 2025-11-22 02:11:35 +01:00
Highlight search terms for blog articles
This commit is contained in:
@@ -2,7 +2,6 @@
|
|||||||
import PageLayout from "../../layouts/PageLayout.astro";
|
import PageLayout from "../../layouts/PageLayout.astro";
|
||||||
import { getCollection } from "astro:content";
|
import { getCollection } from "astro:content";
|
||||||
import Date from "../../components/Date.astro";
|
import Date from "../../components/Date.astro";
|
||||||
|
|
||||||
const allArticles = await getCollection("blog");
|
const allArticles = await getCollection("blog");
|
||||||
const sortedArticles = allArticles.sort((a, b) => {
|
const sortedArticles = allArticles.sort((a, b) => {
|
||||||
return b.data.pubDate.valueOf() - a.data.pubDate.valueOf();
|
return b.data.pubDate.valueOf() - a.data.pubDate.valueOf();
|
||||||
@@ -33,9 +32,11 @@ const sortedArticles = allArticles.sort((a, b) => {
|
|||||||
class="max-w-fit text-blue-800 hover:no-underline active:bg-neutral-200 active:dark:bg-neutral-700"
|
class="max-w-fit text-blue-800 hover:no-underline active:bg-neutral-200 active:dark:bg-neutral-700"
|
||||||
href={`/blog/${id}`}
|
href={`/blog/${id}`}
|
||||||
>
|
>
|
||||||
{title}
|
<span id="article-title">{title}</span>
|
||||||
</a>
|
</a>
|
||||||
<p class="not-prose">{description}</p>
|
<p id="article-description" class="not-prose">
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
</article>
|
</article>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -43,44 +44,90 @@ const sortedArticles = allArticles.sort((a, b) => {
|
|||||||
No articles found matching your search.
|
No articles found matching your search.
|
||||||
</p>
|
</p>
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const setupSearch = () => {
|
const setupSearch = () => {
|
||||||
const searchInput = document.querySelector('input[id="article-search"]');
|
const searchInput = document.querySelector('input[id="article-search"]');
|
||||||
const articles = document.querySelectorAll("[data-article-title]");
|
const articles = document.querySelectorAll("[data-article-title]");
|
||||||
const noResults = document.querySelector("#no-results");
|
const noResults = document.querySelector("#no-results");
|
||||||
|
|
||||||
if (!searchInput || !articles.length || !noResults) return;
|
if (!searchInput || !articles.length || !noResults) return;
|
||||||
|
|
||||||
|
const escapeRegex = (str: string) => {
|
||||||
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
};
|
||||||
|
|
||||||
|
const highlightText = (text: string, searchTerms: string[]) => {
|
||||||
|
if (!searchTerms.length || searchTerms.every((term) => !term)) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
let highlightedText = text;
|
||||||
|
for (const term of searchTerms) {
|
||||||
|
if (term) {
|
||||||
|
const regex = new RegExp(`(${escapeRegex(term)})`, "gi");
|
||||||
|
highlightedText = highlightedText.replace(regex, "<mark>$1</mark>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return highlightedText;
|
||||||
|
};
|
||||||
|
|
||||||
searchInput.addEventListener("input", (e) => {
|
searchInput.addEventListener("input", (e) => {
|
||||||
const target = e.target as HTMLInputElement;
|
const target = e.target as HTMLInputElement;
|
||||||
const searchTerm = target.value
|
const searchValue = target.value.trim().replace(/\s+/g, " ");
|
||||||
.trim()
|
const searchTerms = searchValue
|
||||||
.replace(/\s+/g, " ")
|
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.split(" ");
|
.split(" ")
|
||||||
|
.filter((term) => term);
|
||||||
let hasResults = false;
|
let hasResults = false;
|
||||||
|
|
||||||
for (const article of articles) {
|
for (const article of articles) {
|
||||||
const title = article.getAttribute("data-article-title") ?? "";
|
const originalTitle = article.getAttribute("data-article-title") ?? "";
|
||||||
const description =
|
const originalDescription =
|
||||||
article.getAttribute("data-article-description") ?? "";
|
article.getAttribute("data-article-description") ?? "";
|
||||||
|
const titleElement = article.querySelector("#article-title");
|
||||||
|
const descriptionElement = article.querySelector(
|
||||||
|
"#article-description",
|
||||||
|
);
|
||||||
|
|
||||||
const matchesSearch = searchTerm.every(
|
if (!searchValue) {
|
||||||
|
if (titleElement) {
|
||||||
|
titleElement.innerHTML = originalTitle;
|
||||||
|
}
|
||||||
|
if (descriptionElement) {
|
||||||
|
descriptionElement.innerHTML = originalDescription;
|
||||||
|
}
|
||||||
|
article.classList.remove("hidden");
|
||||||
|
hasResults = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchesSearch = searchTerms.every(
|
||||||
(word) =>
|
(word) =>
|
||||||
title.toLowerCase().includes(word) ||
|
originalTitle.toLowerCase().includes(word) ||
|
||||||
description.toLowerCase().includes(word),
|
originalDescription.toLowerCase().includes(word),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (matchesSearch) {
|
if (matchesSearch) {
|
||||||
|
if (titleElement) {
|
||||||
|
titleElement.innerHTML = highlightText(originalTitle, searchTerms);
|
||||||
|
}
|
||||||
|
if (descriptionElement) {
|
||||||
|
descriptionElement.innerHTML = highlightText(
|
||||||
|
originalDescription,
|
||||||
|
searchTerms,
|
||||||
|
);
|
||||||
|
}
|
||||||
article.classList.remove("hidden");
|
article.classList.remove("hidden");
|
||||||
hasResults = true;
|
hasResults = true;
|
||||||
} else {
|
} else {
|
||||||
|
if (titleElement) {
|
||||||
|
titleElement.innerHTML = originalTitle;
|
||||||
|
}
|
||||||
|
if (descriptionElement) {
|
||||||
|
descriptionElement.innerHTML = originalDescription;
|
||||||
|
}
|
||||||
article.classList.add("hidden");
|
article.classList.add("hidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
noResults.classList.toggle("hidden", hasResults);
|
noResults.classList.toggle("hidden", hasResults);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,6 +23,10 @@
|
|||||||
pre code {
|
pre code {
|
||||||
font-family: "Styrene B", monospace;
|
font-family: "Styrene B", monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mark {
|
||||||
|
@apply bg-neutral-200 text-current dark:bg-neutral-600;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Styrene A Font Family */
|
/* Styrene A Font Family */
|
||||||
|
|||||||
Reference in New Issue
Block a user