feat: hide search results container if no keyword is given (#1288)

feat: hide search results container if no keyword is given and add typing
This commit is contained in:
Jimmy
2026-03-05 12:52:06 +01:00
committed by GitHub
parent 0ad02c5c0f
commit a9a14ce815
3 changed files with 45 additions and 24 deletions
+6
View File
@@ -79,3 +79,9 @@
} }
} }
.search-result {
&.hidden {
display: none;
}
}
+38 -23
View File
@@ -26,15 +26,15 @@ const tagsToReplace = {
'…': '…' '…': '…'
}; };
function replaceTag(tag) { function replaceTag(tag: string) {
return tagsToReplace[tag] || tag; return (tagsToReplace as Record<string, string>)[tag] || tag;
} }
function replaceHTMLEnt(str) { function replaceHTMLEnt(str: string) {
return str.replace(/[&<>"]/g, replaceTag); return str.replace(/[&<>"]/g, replaceTag);
} }
function escapeRegExp(string) { function escapeRegExp(string: string) {
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&');
} }
@@ -45,13 +45,21 @@ class Search {
private list: HTMLDivElement; private list: HTMLDivElement;
private resultTitle: HTMLHeadElement; private resultTitle: HTMLHeadElement;
private resultTitleTemplate: string; private resultTitleTemplate: string;
private container: HTMLDivElement;
constructor({ form, input, list, resultTitle, resultTitleTemplate }) { constructor({ form, input, list, resultTitle, resultTitleTemplate }: {
form: HTMLFormElement,
input: HTMLInputElement,
list: HTMLDivElement,
resultTitle: HTMLHeadingElement,
resultTitleTemplate: string
}) {
this.form = form; this.form = form;
this.input = input; this.input = input;
this.list = list; this.list = list;
this.resultTitle = resultTitle; this.resultTitle = resultTitle;
this.resultTitleTemplate = resultTitleTemplate; this.resultTitleTemplate = resultTitleTemplate;
this.container = list.parentElement as HTMLDivElement;
/// Check if there's already value in the search input /// Check if there's already value in the search input
if (this.input.value.trim() !== '') { if (this.input.value.trim() !== '') {
@@ -203,16 +211,18 @@ class Search {
const endTime = performance.now(); const endTime = performance.now();
this.resultTitle.innerText = this.generateResultTitle(results.length, ((endTime - startTime) / 1000).toPrecision(1)); this.resultTitle.innerText = this.generateResultTitle(results.length, ((endTime - startTime) / 1000).toPrecision(1));
this.container?.classList.remove('hidden');
} }
private generateResultTitle(resultLen, time) { private generateResultTitle(resultLen: number, time: string) {
return this.resultTitleTemplate.replace("#PAGES_COUNT", resultLen).replace("#TIME_SECONDS", time); return this.resultTitleTemplate.replace("#PAGES_COUNT", resultLen.toString()).replace("#TIME_SECONDS", time);
} }
public async getData() { public async getData() {
if (!this.data) { if (!this.data) {
/// Not fetched yet /// Not fetched yet
const jsonURL = this.form.dataset.json; const jsonURL = this.form.dataset.json as string;
this.data = await fetch(jsonURL).then(res => res.json()); this.data = await fetch(jsonURL).then(res => res.json());
const parser = new DOMParser(); const parser = new DOMParser();
@@ -227,7 +237,7 @@ class Search {
private bindSearchForm() { private bindSearchForm() {
let lastSearch = ''; let lastSearch = '';
const eventHandler = (e) => { const eventHandler = (e: Event) => {
e.preventDefault(); e.preventDefault();
const keywords = this.input.value.trim(); const keywords = this.input.value.trim();
@@ -251,6 +261,7 @@ class Search {
private clear() { private clear() {
this.list.innerHTML = ''; this.list.innerHTML = '';
this.resultTitle.innerText = ''; this.resultTitle.innerText = '';
this.container.classList.add('hidden');
} }
private bindQueryStringChange() { private bindQueryStringChange() {
@@ -261,7 +272,7 @@ class Search {
private handleQueryString() { private handleQueryString() {
const pageURL = new URL(window.location.toString()); const pageURL = new URL(window.location.toString());
const keywords = pageURL.searchParams.get('keyword'); const keywords = pageURL.searchParams.get('keyword') || '';
this.input.value = keywords; this.input.value = keywords;
if (keywords) { if (keywords) {
@@ -291,19 +302,21 @@ class Search {
} }
public static render(item: pageData) { public static render(item: pageData) {
return <article> return (
<a href={item.permalink}> <article>
<div class="article-details"> <a href={item.permalink}>
<h2 class="article-title" dangerouslySetInnerHTML={{ __html: item.title }}></h2> <div class="article-details">
<section class="article-preview" dangerouslySetInnerHTML={{ __html: item.preview }}></section> <h2 class="article-title" dangerouslySetInnerHTML={{ __html: item.title }}></h2>
</div> <section class="article-preview" dangerouslySetInnerHTML={{ __html: item.preview }}></section>
{item.image &&
<div class="article-image">
<img src={item.image} loading="lazy" />
</div> </div>
} {item.image &&
</a> <div class="article-image">
</article>; <img src={item.image} loading="lazy" />
</div>
}
</a>
</article>
);
} }
} }
@@ -316,10 +329,12 @@ declare global {
window.addEventListener('load', () => { window.addEventListener('load', () => {
setTimeout(function () { setTimeout(function () {
const searchForm = document.querySelector('.search-form') as HTMLFormElement, const searchForm = document.querySelector('.search-form') as HTMLFormElement,
searchInput = searchForm.querySelector('input') as HTMLInputElement, searchInput = searchForm?.querySelector('input') as HTMLInputElement,
searchResultList = document.querySelector('.search-result--list') as HTMLDivElement, searchResultList = document.querySelector('.search-result--list') as HTMLDivElement,
searchResultTitle = document.querySelector('.search-result--title') as HTMLHeadingElement; searchResultTitle = document.querySelector('.search-result--title') as HTMLHeadingElement;
if (!searchForm || !searchInput || !searchResultList || !searchResultTitle) return;
new Search({ new Search({
form: searchForm, form: searchForm,
input: searchInput, input: searchInput,
+1 -1
View File
@@ -16,7 +16,7 @@
</button> </button>
</form> </form>
<div class="search-result"> <div class="search-result hidden">
<h3 class="search-result--title section-title"></h3> <h3 class="search-result--title section-title"></h3>
<div class="search-result--list article-list--compact"></div> <div class="search-result--list article-list--compact"></div>
</div> </div>