Search/static/js/autocomplete.js
2024-11-20 14:57:55 +01:00

122 lines
4.3 KiB
JavaScript

/*
This script is responsible for fetching search suggestions when the user types in the search bar. It also shows and hides the search suggestions wrapper.
*/
document.addEventListener('DOMContentLoaded', () => {
const searchInput = document.getElementById('search-input');
const searchWrapper = document.querySelector('.wrapper, .wrapper-results');
const resultsWrapper = document.querySelector('.autocomplete');
let suggestions = [];
let currentIndex = -1; // Keep track of the currently selected suggestion
// Fetch suggestions from the server
async function getSuggestions(query) {
try {
const response = await fetch(`/suggestions?q=${encodeURIComponent(query)}`);
const data = await response.json();
return data[1]; // Return only the array of suggestion strings
} catch (error) {
console.error('Error fetching suggestions:', error);
return [];
}
}
// Function to render results
function renderSuggestions(results) {
if (!results || !results.length) {
searchWrapper.classList.remove('wrapper-searching');
resultsWrapper.innerHTML = '';
return;
}
let content = '';
results.forEach((item, index) => {
content += `<li data-index="${index}">${item}</li>`;
});
resultsWrapper.innerHTML = `<ul>${content}</ul>`;
searchWrapper.classList.add('wrapper-searching');
}
// Fetch suggestions when input is focused
searchInput.addEventListener('focus', async () => {
const query = searchInput.value.trim();
suggestions = await getSuggestions(query);
renderSuggestions(suggestions);
currentIndex = -1;
});
// Handle input event
searchInput.addEventListener('input', async () => {
const query = searchInput.value.trim();
if (query.length > 0) {
suggestions = await getSuggestions(query);
} else {
suggestions = [];
}
currentIndex = -1; // Reset index when new results come in
renderSuggestions(suggestions);
});
// Handle keydown events for navigation
searchInput.addEventListener('keydown', (event) => {
const items = resultsWrapper.querySelectorAll('li');
if (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'Tab') {
event.preventDefault();
// Remove 'selected' class from the current item
if (items[currentIndex]) {
items[currentIndex].classList.remove('selected');
}
if (event.key === 'ArrowUp') {
currentIndex = (currentIndex > 0) ? currentIndex - 1 : items.length - 1;
} else {
currentIndex = (currentIndex + 1) % items.length;
}
// Add 'selected' class to the new item and update the input value
if (items[currentIndex]) {
items[currentIndex].classList.add('selected');
searchInput.value = items[currentIndex].textContent;
}
} else if (event.key === 'Enter') {
if (currentIndex > -1 && items[currentIndex]) {
event.preventDefault();
selectSuggestion(items[currentIndex]);
}
}
});
// Function to handle suggestion selection
function selectSuggestion(item) {
const query = item.textContent;
searchInput.value = query;
resultsWrapper.innerHTML = '';
searchWrapper.classList.remove('wrapper-searching');
// Submit the form or navigate to search results
const form = searchInput.closest('form');
if (form) {
form.submit();
} else {
window.location.href = `/search?q=${encodeURIComponent(query)}&t=web`;
}
}
// Handle clicks on search suggestions
resultsWrapper.addEventListener('click', (event) => {
if (event.target.tagName === 'LI') {
selectSuggestion(event.target);
}
});
// Close the suggestions when clicking outside
document.addEventListener('click', (event) => {
if (!searchWrapper.contains(event.target)) {
searchWrapper.classList.remove('wrapper-searching');
resultsWrapper.innerHTML = '';
currentIndex = -1;
}
});
});