Files
thrillwiki_django_no_react/backend/static/js/search-accessibility.js
pacnpal edcd8f2076 Add secret management guide, client-side performance monitoring, and search accessibility enhancements
- Introduced a comprehensive Secret Management Guide detailing best practices, secret classification, development setup, production management, rotation procedures, and emergency protocols.
- Implemented a client-side performance monitoring script to track various metrics including page load performance, paint metrics, layout shifts, and memory usage.
- Enhanced search accessibility with keyboard navigation support for search results, ensuring compliance with WCAG standards and improving user experience.
2025-12-23 16:41:42 -05:00

76 lines
2.8 KiB
JavaScript

/**
* Search Results Keyboard Navigation
* Handles Arrow Up/Down, Enter, and Escape keys for accessible search
*
* This module enhances search inputs with keyboard navigation for WCAG compliance:
* - Arrow Down: Navigate to next search result
* - Arrow Up: Navigate to previous search result (or back to input)
* - Enter: Select current result (navigate to link)
* - Escape: Close search results and blur input
*
* Usage:
* The script automatically initializes on DOMContentLoaded for all search inputs
* with hx-target attribute pointing to a results container.
*
* HTMX Integration:
* Results should include role="option" on each selectable item.
* The script listens for htmx:afterSwap to reinitialize when results change.
*/
document.addEventListener('DOMContentLoaded', () => {
const searchInputs = document.querySelectorAll('input[type="search"]');
searchInputs.forEach(input => {
const resultsContainer = document.querySelector(input.getAttribute('hx-target'));
if (!resultsContainer) return;
let currentIndex = -1;
input.addEventListener('keydown', (e) => {
const results = resultsContainer.querySelectorAll('[role="option"]');
if (results.length === 0) return;
switch(e.key) {
case 'ArrowDown':
e.preventDefault();
currentIndex = Math.min(currentIndex + 1, results.length - 1);
updateSelection(results, currentIndex);
break;
case 'ArrowUp':
e.preventDefault();
currentIndex = Math.max(currentIndex - 1, -1);
if (currentIndex === -1) {
input.focus();
} else {
updateSelection(results, currentIndex);
}
break;
case 'Enter':
if (currentIndex >= 0) {
e.preventDefault();
results[currentIndex].querySelector('a').click();
}
break;
case 'Escape':
e.preventDefault();
resultsContainer.innerHTML = '';
input.blur();
break;
}
});
function updateSelection(results, index) {
results.forEach((result, i) => {
if (i === index) {
result.setAttribute('aria-selected', 'true');
result.classList.add('bg-accent');
result.scrollIntoView({ block: 'nearest' });
} else {
result.setAttribute('aria-selected', 'false');
result.classList.remove('bg-accent');
}
});
}
});
});