|
1 | | -//This is the JavaScript function of the search bar of the library |
| 1 | +// This is the JavaScript function of the search bar of the library. |
| 2 | +// A book matches when every word typed appears somewhere in the book's |
| 3 | +// title, author(s), description, or base course name (the `data-search` |
| 4 | +// attribute built in the template holds all four fields, lower-cased). |
2 | 5 |
|
3 | 6 | function search_book() { |
4 | | - let input = document.getElementById("searchbar").value; |
5 | | - input = input.toLowerCase(); |
6 | | - let x = document.getElementsByClassName("book_title"); |
7 | | - let y = document.getElementsByClassName("book_descript"); |
| 7 | + let input = document.getElementById("searchbar").value.toLowerCase(); |
| 8 | + let words = input.split(/\s+/).filter((w) => w.length > 0); |
8 | 9 |
|
9 | | - for (i = 0; i < x.length; i++) { |
10 | | - if ( |
11 | | - x[i].innerHTML.toLowerCase().includes(input) || |
12 | | - y[i].innerHTML.toLowerCase().includes(input) |
13 | | - ) { |
14 | | - x[i].parentElement.style.display = "list-item"; |
15 | | - x[i].parentElement.previousElementSibling.style.display = |
16 | | - "list-item"; |
17 | | - y[i].parentElement.style.display = "list-item"; |
18 | | - y[i].parentElement.previousElementSibling.style.display = |
19 | | - "list-item"; |
20 | | - } else { |
21 | | - x[i].parentElement.style.display = "none"; |
22 | | - y[i].parentElement.style.display = "none"; |
23 | | - } |
24 | | - } |
| 10 | + let sections = document.querySelectorAll("details"); |
| 11 | + let totalMatches = 0; |
25 | 12 |
|
26 | | - // remove section headings when searching - restore when the search box is empty |
27 | | - let sections = document.querySelectorAll(".sectionName"); |
28 | 13 | sections.forEach((sec) => { |
29 | | - sec.style.display = input ? "none" : "list-item"; |
| 14 | + let entries = sec.querySelectorAll(".library_entry"); |
| 15 | + let sectionMatches = 0; |
| 16 | + |
| 17 | + entries.forEach((entry) => { |
| 18 | + let haystack = entry.getAttribute("data-search") || ""; |
| 19 | + // Match only when every word is found somewhere in the fields. |
| 20 | + let matches = words.every((w) => haystack.includes(w)); |
| 21 | + entry.style.display = matches ? "" : "none"; |
| 22 | + if (matches) { |
| 23 | + sectionMatches++; |
| 24 | + } |
| 25 | + }); |
| 26 | + totalMatches += sectionMatches; |
| 27 | + |
| 28 | + if (words.length === 0) { |
| 29 | + // Empty box: restore the original collapsed sections. |
| 30 | + sec.style.display = ""; |
| 31 | + sec.removeAttribute("open"); |
| 32 | + } else if (sectionMatches > 0) { |
| 33 | + // Open any section that has a match, even if it was closed. |
| 34 | + sec.style.display = ""; |
| 35 | + sec.setAttribute("open", ""); |
| 36 | + } else { |
| 37 | + // Hide sections with no matches while searching. |
| 38 | + sec.style.display = "none"; |
| 39 | + } |
30 | 40 | }); |
| 41 | + |
| 42 | + let noResults = document.getElementById("search_no_results"); |
| 43 | + if (noResults) { |
| 44 | + noResults.style.display = |
| 45 | + words.length > 0 && totalMatches === 0 ? "block" : "none"; |
| 46 | + } |
31 | 47 | } |
0 commit comments