Skip to content

Commit 77aa4f1

Browse files
authored
158 tatsatjha update search results (#163)
* [#158] add a custom index.json to index posts with authors and dates * [#158] add custom search json to display authors and dates * [#158] allow search by author or search by date functionality * [#158] change from showing text content at the start of the search term to showing the first 400 characters of text for each post, similar to the normal snippets
1 parent 8ec75bb commit 77aa4f1

File tree

2 files changed

+269
-0
lines changed

2 files changed

+269
-0
lines changed

assets/js/search.js

+262
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
function initializeSearch(index) {
2+
const searchKeys = ['title', 'link', 'summary', 'body', 'date', 'author', 'id', 'section', 'tags'];
3+
4+
const searchPageElement = elem('#searchpage');
5+
6+
const searchOptions = {
7+
ignoreLocation: true,
8+
findAllMatches: true,
9+
includeScore: true,
10+
shouldSort: true,
11+
keys: searchKeys,
12+
threshold: 0.0
13+
};
14+
15+
index = new Fuse(index, searchOptions);
16+
17+
function minQueryLen(query) {
18+
query = query.trim();
19+
const queryIsFloat = parseFloat(query);
20+
const minimumQueryLength = queryIsFloat ? 1 : 2;
21+
return minimumQueryLength;
22+
}
23+
24+
function searchResults(results = [], query = "", passive = false) {
25+
let resultsFragment = new DocumentFragment();
26+
let showResults = elem('.search_results');
27+
if (passive) {
28+
showResults = searchPageElement;
29+
}
30+
emptyEl(showResults);
31+
32+
const queryLen = query.length;
33+
const requiredQueryLen = minQueryLen(query);
34+
35+
if (results.length && queryLen >= requiredQueryLen) {
36+
let resultsTitle = createEl('h3');
37+
resultsTitle.className = 'search_title';
38+
resultsTitle.innerText = quickLinks;
39+
40+
let goBackButton = createEl('button');
41+
goBackButton.textContent = 'Go Back';
42+
goBackButton.className = goBackClass;
43+
if (passive) {
44+
resultsTitle.innerText = searchResultsLabel;
45+
}
46+
if (!searchPageElement) {
47+
results = results.slice(0, 8);
48+
} else {
49+
resultsFragment.appendChild(goBackButton);
50+
results = results.slice(0, 12);
51+
}
52+
resultsFragment.appendChild(resultsTitle);
53+
54+
results.forEach(function (result) {
55+
let item = createEl('a');
56+
item.href = `${result.link}?query=${query}`;
57+
item.className = 'search_result';
58+
item.style.order = result.score;
59+
if (passive) {
60+
console.log(result);
61+
pushClass(item, 'passive');
62+
let itemTitle = createEl('h3');
63+
itemTitle.textContent = result.title;
64+
item.appendChild(itemTitle);
65+
66+
let itemAuthor = createEl('p');
67+
itemAuthor.textContent = "By: "
68+
itemAuthor.textContent += result.author;
69+
item.appendChild(itemAuthor);
70+
71+
const date = new Date(result.date);
72+
73+
const options = {
74+
year: 'numeric',
75+
month: 'long',
76+
day: 'numeric'
77+
};
78+
79+
const formattedDate = new Intl.DateTimeFormat('en-US', options).format(date);
80+
81+
let itemDate = createEl('p');
82+
itemDate.textContent = formattedDate;
83+
item.appendChild(itemDate);
84+
85+
let itemDescription = createEl('p');
86+
console.log(result)
87+
88+
itemDescription.textContent = `${result.body.substring(0, 400)}...`;
89+
item.appendChild(itemDescription);
90+
} else {
91+
item.textContent = result.title;
92+
}
93+
resultsFragment.appendChild(item);
94+
});
95+
}
96+
97+
if (queryLen >= requiredQueryLen) {
98+
if (!results.length) {
99+
showResults.innerHTML = `<span class="search_result">${noMatchesFound}</span>`;
100+
}
101+
} else {
102+
showResults.innerHTML = `<label for="find" class="search_result">${queryLen > 1 ? shortSearchQuery : typeToSearch}</label>`
103+
}
104+
105+
showResults.appendChild(resultsFragment);
106+
}
107+
108+
function search(searchTerm, scope = null, passive = false) {
109+
if (searchTerm.length) {
110+
let rawResults = index.search(searchTerm);
111+
rawResults = rawResults.map(function (result) {
112+
const score = result.score;
113+
const resultItem = result.item;
114+
resultItem.score = (parseFloat(score) * 50).toFixed(0);
115+
return resultItem;
116+
})
117+
118+
if (scope) {
119+
rawResults = rawResults.filter(resultItem => {
120+
return resultItem.section == scope;
121+
});
122+
}
123+
124+
passive ? searchResults(rawResults, searchTerm, true) : searchResults(rawResults, searchTerm);
125+
126+
} else {
127+
passive ? searchResults([], "", true) : searchResults();
128+
}
129+
}
130+
131+
function liveSearch() {
132+
const searchField = elem(searchFieldClass);
133+
134+
if (searchField) {
135+
const searchScope = searchField.dataset.scope;
136+
searchField.addEventListener('input', function () {
137+
const searchTerm = searchField.value.trim().toLowerCase();
138+
search(searchTerm, searchScope);
139+
});
140+
141+
if (!searchPageElement) {
142+
searchField.addEventListener('search', function () {
143+
const searchTerm = searchField.value.trim().toLowerCase();
144+
if (searchTerm.length) {
145+
const scopeParameter = searchScope ? `&scope=${searchScope}` : '';
146+
window.location.href = new URL(baseURL + `search/?query=${searchTerm}${scopeParameter}`).href;
147+
}
148+
});
149+
}
150+
}
151+
}
152+
153+
function passiveSearch() {
154+
if (searchPageElement) {
155+
const searchTerm = findQuery();
156+
const searchScope = findQuery('scope');
157+
// search actively after search page has loaded
158+
const searchField = elem(searchFieldClass);
159+
160+
search(searchTerm, searchScope, true);
161+
162+
if (searchField) {
163+
searchField.addEventListener('input', function () {
164+
const searchTerm = searchField.value.trim().toLowerCase();
165+
search(searchTerm, true);
166+
wrapText(searchTerm, main);
167+
});
168+
}
169+
}
170+
}
171+
172+
function hasSearchResults() {
173+
const searchResults = elem('.results');
174+
if (searchResults) {
175+
const body = searchResults.innerHTML.length;
176+
return [searchResults, body];
177+
}
178+
return false
179+
}
180+
181+
function clearSearchResults() {
182+
let searchResults = hasSearchResults();
183+
if (searchResults) {
184+
searchResults = searchResults[0];
185+
searchResults.innerHTML = "";
186+
// clear search field
187+
const searchField = elem(searchFieldClass);
188+
searchField.value = "";
189+
}
190+
}
191+
192+
function onEscape(fn) {
193+
window.addEventListener('keydown', function (event) {
194+
if (event.code === "Escape") {
195+
fn();
196+
}
197+
});
198+
}
199+
200+
let main = elem('main');
201+
if (!main) {
202+
main = elem('.main');
203+
}
204+
205+
searchPageElement ? false : liveSearch();
206+
passiveSearch();
207+
208+
highlightSearchTerms(findQuery(), '.post_body', 'mark', 'search-term');
209+
210+
onEscape(clearSearchResults);
211+
212+
window.addEventListener('click', function (event) {
213+
const target = event.target;
214+
const isSearch = target.closest(searchClass) || target.matches(searchClass);
215+
if (!isSearch && !searchPageElement) {
216+
clearSearchResults();
217+
}
218+
});
219+
}
220+
221+
function highlightSearchTerms(search, context, wrapper = 'mark', cssClass = '') {
222+
const query = findQuery()
223+
if (query) {
224+
225+
let container = elem(context);
226+
let reg = new RegExp("(" + search + ")", "gi");
227+
228+
function searchInNode(parentNode, search) {
229+
forEach(parentNode, function (node) {
230+
if (node.nodeType === 1) {
231+
searchInNode(node, search);
232+
} else if (
233+
node.nodeType === 3 &&
234+
reg.test(node.nodeValue)
235+
) {
236+
let string = node.nodeValue.replace(reg, `<${wrapper} class="${cssClass}">$1</${wrapper}>`);
237+
let span = document.createElement("span");
238+
span.dataset.searched = "true";
239+
span.innerHTML = string;
240+
parentNode.replaceChild(span, node);
241+
}
242+
});
243+
};
244+
245+
searchInNode(container, search);
246+
247+
}
248+
}
249+
250+
window.addEventListener('load', function () {
251+
const pageLanguage = elem('body').dataset.lang;
252+
const searchIndexLangSlug = pageLanguage === defaultSiteLanguage ? '' : `${pageLanguage}/`;
253+
let searchIndex = `${searchIndexLangSlug}index.json`;
254+
searchIndex = new URL(`${baseURL}${searchIndex}`).href;
255+
fetch(searchIndex)
256+
.then(response => response.json())
257+
.then(function (data) {
258+
data = data.length ? data : [];
259+
initializeSearch(data);
260+
})
261+
.catch((error) => console.error(error));
262+
});

layouts/_default/index.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{{- $.Scratch.Add "index" slice -}}
2+
{{- range .Site.Pages -}}
3+
{{- if ne .Type "search" -}}
4+
{{- $.Scratch.Add "index" (dict "title" .Title "body" .Plain "author" .Params.author "date" .Params.date "link" .Permalink "section" .Section "tags" .Params.tags) -}}
5+
{{- end -}}
6+
{{- end -}}
7+
{{- jsonify (uniq ($.Scratch.Get "index")) -}}

0 commit comments

Comments
 (0)