Fix navigation menu closing too quickly with transition delay and keyboard support
ci/woodpecker/push/woodpecker Pipeline failed Details

This commit is contained in:
colin 2025-07-06 11:20:32 -04:00
parent a1e2afabb5
commit c4a45ef8fd
2 changed files with 96 additions and 1 deletions

View File

@ -79,10 +79,99 @@ document.addEventListener('DOMContentLoaded', function() {
const footerElement = document.getElementById('footer-include'); const footerElement = document.getElementById('footer-include');
if (headerElement) { if (headerElement) {
includeHTML('header-include', '/includes/header.html', setActiveNavItem); includeHTML('header-include', '/includes/header.html', () => {
setActiveNavItem();
setupNavDropdowns();
});
} }
if (footerElement) { if (footerElement) {
includeHTML('footer-include', '/includes/footer.html'); includeHTML('footer-include', '/includes/footer.html');
} }
// Setup dropdown behavior with delay
function setupNavDropdowns() {
const dropdowns = document.querySelectorAll('.main-nav .dropdown');
let timeoutId;
dropdowns.forEach(dropdown => {
// Mouse interactions
dropdown.addEventListener('mouseenter', () => {
clearTimeout(timeoutId);
dropdowns.forEach(d => {
if (d !== dropdown) {
d.querySelector('.dropdown-content').style.display = 'none';
d.querySelector('.dropdown-content').style.opacity = '0';
d.querySelector('.dropdown-content').style.visibility = 'hidden';
}
});
const dropdownContent = dropdown.querySelector('.dropdown-content');
dropdownContent.style.display = 'block';
// Small delay to allow CSS transition to work properly
setTimeout(() => {
dropdownContent.style.opacity = '1';
dropdownContent.style.visibility = 'visible';
}, 10);
});
dropdown.addEventListener('mouseleave', () => {
const dropdownContent = dropdown.querySelector('.dropdown-content');
// Add delay before hiding the dropdown
timeoutId = setTimeout(() => {
dropdownContent.style.opacity = '0';
dropdownContent.style.visibility = 'hidden';
// Wait for transition to complete before changing display
setTimeout(() => {
if (dropdownContent.style.opacity === '0') {
dropdownContent.style.display = 'none';
}
}, 200);
}, 300); // 300ms delay before starting to close
});
// Keyboard interactions
const dropdownLink = dropdown.querySelector('a');
dropdownLink.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
e.preventDefault();
const dropdownContent = dropdown.querySelector('.dropdown-content');
dropdownContent.style.display = 'block';
setTimeout(() => {
dropdownContent.style.opacity = '1';
dropdownContent.style.visibility = 'visible';
// Focus the first link in the dropdown
const firstLink = dropdownContent.querySelector('a');
if (firstLink) firstLink.focus();
}, 10);
}
});
// Add keyboard navigation for dropdown items
const dropdownLinks = dropdown.querySelectorAll('.dropdown-content a');
dropdownLinks.forEach((link, index) => {
link.addEventListener('keydown', (e) => {
if (e.key === 'ArrowDown') {
e.preventDefault();
const nextLink = dropdownLinks[index + 1] || dropdownLinks[0];
nextLink.focus();
} else if (e.key === 'ArrowUp') {
e.preventDefault();
const prevLink = dropdownLinks[index - 1] || dropdownLinks[dropdownLinks.length - 1];
prevLink.focus();
} else if (e.key === 'Escape') {
e.preventDefault();
dropdown.querySelector('a').focus();
const dropdownContent = dropdown.querySelector('.dropdown-content');
dropdownContent.style.opacity = '0';
dropdownContent.style.visibility = 'hidden';
setTimeout(() => {
dropdownContent.style.display = 'none';
}, 200);
}
});
});
});
}
}); });

View File

@ -302,11 +302,17 @@ hr {
border-radius: 4px; border-radius: 4px;
padding: 0.5rem 0; padding: 0.5rem 0;
margin-top: 0.5rem; margin-top: 0.5rem;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, visibility 0s linear 0.2s;
} }
.main-nav .dropdown:hover .dropdown-content, .main-nav .dropdown:hover .dropdown-content,
.main-nav .dropdown:focus-within .dropdown-content { .main-nav .dropdown:focus-within .dropdown-content {
display: block; display: block;
opacity: 1;
visibility: visible;
transition: opacity 0.2s ease, visibility 0s linear 0s;
} }
.main-nav .dropdown-content a { .main-nav .dropdown-content a {