dev21w34g

This commit is contained in:
Victor Westerlund 2021-10-29 16:48:01 +02:00
parent f2f1a3b013
commit a48d645f12
6 changed files with 32 additions and 13 deletions

View file

@ -6,18 +6,21 @@ export default class Search {
this.lastQuery = ""; this.lastQuery = "";
this.throttle = null; this.throttle = null;
this.controller = null; // AbortController will be assigned here
this.results = results; this.results = results;
this.input = input; this.input = input;
this.input?.addEventListener("keyup",event => this.keyEvent(event)) ?? false; this.input?.addEventListener("keyup",event => this.keyEvent(event)) ?? false;
} }
// Destroy the result DOM tree
clearResults() { clearResults() {
while(this.results.firstChild) { while(this.results.firstChild) {
this.results.removeChild(this.results.lastChild); this.results.removeChild(this.results.lastChild);
} }
} }
// Display output as HTML
output(html) { output(html) {
this.clearResults(); this.clearResults();
if(typeof html === "string") { if(typeof html === "string") {
@ -27,6 +30,7 @@ export default class Search {
this.results.appendChild(html); this.results.appendChild(html);
} }
// Display a status message in a paragraph
status(text,classList = false) { status(text,classList = false) {
const element = document.createElement("p"); const element = document.createElement("p");
if(classList !== false) { if(classList !== false) {
@ -37,12 +41,15 @@ export default class Search {
this.output(element); this.output(element);
} }
// Fetch search results from endpoint
async search(query) { async search(query) {
const url = new URL(this.endpoint); const url = new URL(this.endpoint);
url.searchParams.set("q",query); url.searchParams.set("q",query);
const timeout = new Promise(reject => setTimeout(() => reject("Request timed out"),3000)); const timeout = new Promise(reject => setTimeout(() => reject("Request timed out"),3000));
// Fetch response from server
const api = fetch(url,{ const api = fetch(url,{
signal: this.controller.signal,
headers: { headers: {
"Content-Type": "text/html" "Content-Type": "text/html"
} }
@ -51,29 +58,33 @@ export default class Search {
const result = Promise.race([api,timeout]); const result = Promise.race([api,timeout]);
result.then(response => { result.then(response => {
if(!response.ok) { if(!response.ok) {
console.error("Response from server:",response); this.status("oh no, something went wrong","error");
throw new Error("Invalid response from server"); throw new Error("Invalid response from server");
} }
return response.text(); return response.text();
}) })
.then(html => this.output(html)) .then(html => this.output(html))
.catch(error => this.status(error,"error")); .catch(error => {});
} }
// Wait until the user stops typing for a few miliseconds // Wait until the user stops typing for a few miliseconds
queue(query) { queue(query) {
clearTimeout(this.throttle); clearTimeout(this.throttle);
this.controller = new AbortController(); // Spawn a new AbortController for each fetch
this.throttle = setTimeout(() => this.search(query),500); this.throttle = setTimeout(() => this.search(query),500);
} }
keyEvent(event) { keyEvent(event) {
const query = event.target.value; const query = event.target.value;
// Don't do the search thing if query is too weak
if(query.length < 1) { if(query.length < 1) {
this.controller.abort(); // Abort queued search
this.lastQuery = ""; this.lastQuery = "";
this.status("search results will appear here as you type"); this.status("search results will appear here as you type");
return; return;
} }
// Pressing a modifier key (Ctrl, Shift etc.) doesn't change the query
if(query === this.lastQuery) { if(query === this.lastQuery) {
return false; return false;
} }

View file

@ -0,0 +1,6 @@
const search = document.getElementById("search").children[0];
const results = document.getElementById("results").children[0];
search.style.setProperty("display","none");
results.classList.add("error");
results.innerText = "Sorry, your browser isn't supported yet";

View file

@ -1,7 +0,0 @@
import { default as Search } from "./modules/Search.mjs";
const searchBox = document.getElementById("search")?.children[0] ?? false;
const resultsContainer = document.getElementById("results");
new Search(searchBox,resultsContainer);
window.addEventListener("keydown",() => searchBox.focus());

View file

@ -0,0 +1,9 @@
import { default as Search } from "./modules/Search.mjs";
const searchBox = document.getElementById("search")?.getElementsByTagName("input")[0] ?? false;
const resultsContainer = document.getElementById("results");
new Search(searchBox,resultsContainer);
// Set focus on searchbox when typing from anywhere
window.addEventListener("keydown",() => searchBox.focus());

View file

@ -9,7 +9,6 @@
<link rel="icon" href="assets/img/favicon.png"> <link rel="icon" href="assets/img/favicon.png">
<link rel="stylesheet" href="assets/css/style.css"> <link rel="stylesheet" href="assets/css/style.css">
<link rel="stylesheet" href="assets/css/search.css"> <link rel="stylesheet" href="assets/css/search.css">
<link rel="preload" as="font" href="assets/fonts/RobotoMono-Bold.woff2">
</head> </head>
<body> <body>
<header> <header>
@ -21,6 +20,7 @@
<div id="results"> <div id="results">
<p>search results will appear here as you type</p> <p>search results will appear here as you type</p>
</div> </div>
<script type="module" src="assets/js/search.js"></script> <script type="module" src="assets/js/search.mjs"></script>
<script nomodule defer src="assets/js/noscript.js"></script>
</body> </body>
</html> </html>

View file

@ -27,7 +27,7 @@
// Perform a seach on the given query and return the results as an array // Perform a seach on the given query and return the results as an array
private function get_results() { private function get_results() {
$sql = "SELECT template,title,content,href FROM `search` WHERE `title` LIKE '%{$this->query}%'"; $sql = "SELECT template,title,content,href FROM `search` WHERE `title` LIKE '%{$this->query}%' OR `content` LIKE '%{$this->query}%'";
$rows = $this->get_rows($sql); $rows = $this->get_rows($sql);
return $rows; return $rows;
} }
@ -47,7 +47,7 @@
$results = $this->get_results(); $results = $this->get_results();
if(count($results) < 1) { if(count($results) < 1) {
$results[] = ["message","info","no results :("]; $results[] = ["message","info","no results 😞"];
} }
// Load HTML and format each response from template // Load HTML and format each response from template