Created
January 16, 2024 08:59
-
-
Save bartwttewaall/4c257ea0bf9379e25b5b975b50124f36 to your computer and use it in GitHub Desktop.
Toggle submenu on mouseover with debounced close on mouseout
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { debounce } from 'ts-debounce'; | |
export function initLinksWithSubmenu(el: HTMLElement) { | |
// find all links with children | |
const links = el.querySelectorAll<HTMLElement>("li.has-children > a"); | |
let mouseover = false; | |
const debouncedClose = debounce(async (el: HTMLElement) => toggle(el, false), 1500); | |
const toggle = (el?: HTMLElement, value?: boolean) => { | |
close(el, true); | |
const li = el?.closest("li"); | |
if (value === undefined) li?.classList.toggle("open"); | |
else li?.classList.toggle("open", value); | |
} | |
const close = (el?: HTMLElement, others = false) => { | |
links.forEach((link) => { | |
if (others && link === el) return; | |
else link.closest("li")?.classList.remove("open"); | |
}); | |
}; | |
links.forEach((link) => { | |
const submenu = link.nextElementSibling; | |
// we're listening on a click on the whole element to toggle 'open' | |
link.addEventListener("click", event => { | |
event.preventDefault(); | |
if (!mouseover) toggle(event.target as HTMLElement); | |
}); | |
link.addEventListener("mouseover", (event) => { | |
mouseover = true; | |
toggle(event.target as HTMLElement, true); | |
}); | |
link.addEventListener("mouseout", (event) => { | |
mouseover = false; | |
debouncedClose(event.currentTarget as HTMLElement).catch(_ => {}); | |
}); | |
submenu?.addEventListener("mouseover", (event) => { | |
mouseover = true; | |
debouncedClose.cancel(); | |
toggle(event.currentTarget as HTMLElement, true); | |
}); | |
submenu?.addEventListener("mouseout", (event) => { | |
mouseover = false; | |
debouncedClose(event.currentTarget as HTMLElement).catch(_ => {}); | |
}); | |
}); | |
// check if we click outside the navigation and then close it | |
document.addEventListener("click", (event) => { | |
if (!(event.target as HTMLElement).closest("[data-links]")) close(); | |
}); | |
} | |
export function initLanguageSwitcher(root: HTMLElement) { | |
const button = root.querySelector<HTMLElement>("[data-language-button]"); | |
const submenu = root.querySelector<HTMLElement>("[data-language-menu]"); | |
let mouseover = false; | |
const debouncedClose = debounce(async () => toggle(false), 1500); | |
const toggle = (value?: boolean) => { | |
if (value === undefined) submenu?.classList.toggle("open"); | |
else submenu?.classList.toggle("open", value); | |
} | |
button?.addEventListener("click", (event: MouseEvent) => { | |
event.preventDefault(); | |
if (!mouseover) toggle(); | |
}); | |
button?.addEventListener("mouseover", () => { | |
mouseover = true; | |
toggle(true); | |
}); | |
button?.addEventListener("mouseout", () => { | |
mouseover = false; | |
debouncedClose().catch(_ => {}); | |
}); | |
submenu?.addEventListener("mouseover", () => { | |
mouseover = true; | |
debouncedClose.cancel(); | |
}); | |
submenu?.addEventListener("mouseout", () => { | |
mouseover = false; | |
debouncedClose().catch(_ => {}); | |
}); | |
document.addEventListener("click", (event) => { | |
if (!(event.target as HTMLElement).closest("[data-language-switcher]")) | |
toggle(false); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment