I know maybe it is a very common question, and I've searched, but I didn't find any scenarios like mine, or if I have, it's with frameworks and libraries I cannot use.
I have two elements: an anchor, and a section tag container:
<a clas="triggerMenu" href="#">Trigger</a>
<section class="menuToShow" style="display: none;">Section to show</section>
My goal is that, when I click the trigger anchor link, the section tag element, which is initially hidden, must appear. And just after that, if I detect a click outside my section tag element, this disappears.
I've tried this:
document.querySelector('.triggerMenu').addEventListener("click", (e)=>{
e.preventDefault();
this.showMenu();
});
showMenu(){
this.profileMenuWindow!.style.display = "flex";
detectClickOutside(this.profileMenuWindow!, this.hideProfileMenu.bind(this));
}
hideProfileMenu(){
this.profileMenuWindow!.style.display = "none";
}
and, the detectClickOutside function which is an exportable function from another file:
export function detectClickOutside<T extends HTMLElement>(elementToDetectClickOutside: T, callback: () => void) {
const handleClick = (event: MouseEvent) => {
const target = event.target as Node;
if (!elementToDetectClickOutside.contains(target)) {
callback();
document.removeEventListener('click', handleClick);
}
};
document.addEventListener('click', handleClick);
}
Explanation: I set the listener to the trigger anchor element. When that element get clicked, the profileMenuWindows (the section tag), shows with a display flex. And just after that I call the function detectClickOutside who sets a listener for the entire document checking if elements clicked are outside the profileMenuWindows.
My problem is that the first click is detecting the trigger element as an element outside the menu. Tracking the code, I've detected that it executes fine, showing the menu a millisecond, but then closes it.
Is this approach the best way to open and close a menu triggered by another element? I can't use frameworks to do that (I know it would be easier but I'm on a Symfony project only with TypeScript)
The key is calling
event.stopPropagationinside trigger element's event handler in order to prevent the click event from bubbling up to thedocument.