我知道也许这是一个非常常见的问题,我已经搜索过,但我没有找到任何像我这样的场景,或者即使有,也是我无法使用的框架和库。
我有两个元素:一个锚点和一个部分标签容器:
<a clas="triggerMenu" href="#">Trigger</a>
<section class="menuToShow" style="display: none;">Section to show</section>
我的目标是,当我单击触发锚链接时,最初隐藏的部分标签元素必须出现。就在那之后,如果我检测到在我的部分标签元素之外单击,它就会消失。
我试过这个:
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";
}
以及 detectorClickOutside 函数,它是另一个文件中的可导出函数:
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);
}
说明:我将监听器设置为触发锚点元素。单击该元素时,profileMenuWindows(部分标签)将显示为显示 Flex。之后我调用函数 detectorClickOutside,它为整个文档设置一个侦听器,检查单击的元素是否在 profileMenuWindows 之外。
我的问题是第一次单击将触发元素检测为菜单之外的元素。跟踪代码,我发现它执行得很好,显示菜单一毫秒,但然后关闭它。
这种方法是打开和关闭由另一个元素触发的菜单的最佳方式吗?我无法使用框架来做到这一点(我知道这会更容易,但我只使用 TypeScript 在 Symfony 项目中)
关键是在触发元素的事件处理程序中调用
event.stopPropagation
,以防止单击事件冒泡到document
。
let triggerMenu = document.querySelector('.triggerMenu');
let profileMenuWindow = document.querySelector('.menuToShow');
triggerMenu.addEventListener('click', event => {
event.preventDefault();
// prevent the current event from bubbling up to the document
event.stopPropagation();
this.showMenu();
this.detectClickOutside(profileMenuWindow, this.hideMenu)
});
function showMenu() {
profileMenuWindow.style.display = 'flex';
}
function hideMenu() {
profileMenuWindow.style.display = 'none';
}
// exported function
function detectClickOutside(elementToDetectClickOutside, callback) {
const handleClick = event => {
event.preventDefault();
if (!elementToDetectClickOutside?.isEqualNode(event.target)) {
callback();
document.removeEventListener('click', handleClick);
}
};
document.addEventListener('click', handleClick);
}
<a class="triggerMenu" href="#">Trigger</a>
<section class="menuToShow" style="display: none;">Section to show</section>