如何在 vanilla JS 中检测到最初隐藏的元素外部的点击?

问题描述 投票:0回答:1

我知道也许这是一个非常常见的问题,我已经搜索过,但我没有找到任何像我这样的场景,或者即使有,也是我无法使用的框架和库。

我有两个元素:一个锚点和一个部分标签容器:

<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 项目中)

javascript typescript click listener
1个回答
0
投票

关键是在触发元素的事件处理程序中调用

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>

© www.soinside.com 2019 - 2024. All rights reserved.