单击外部时关闭下拉按钮,但性能良好

Close dropdown button when clicking outside but with good performance

提问人:nicoiscoding 提问时间:8/23/2023 最后编辑:nicoiscoding 更新时间:8/24/2023 访问量:104

问:

我一直在尝试制作一个下拉按钮,当它点击它外面时它会停用,但我没有找到性能良好的好解决方案,我正在使用 Astro 框架制作网站

所以这是我希望它是什么样子的一个例子:例子

这就是我所拥有的:我的网站

这是我的代码:


<nav
class="hidden items-center lg:relative  lg:mt-0 lg:!flex lg:basis-auto"
>
<!-- Desktop Navigation -->
<ul
class="list-style-none mr-auto flex flex-col pl-0 lg:flex-row"
>
    {navData.map(data => {
        if(data.dropdown === true) {
            return (
                <li data-title={data.title} class="sm:block relative dropdowns mx-2 lg:flex ">
                    <button data-title={data.title} class="dropdown text-sm rounded flex gap-1  items-center">
                        {data.title}
                        <svg viewBox="0 0 1024 1024" class="icon w-3" version="1.1" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M903.232 256l56.768 50.432L512 768 64 306.432 120.768 256 512 659.072z" ></path></g></svg>
                    </button>
                    <div data-title={data.title} class="absolute right-0 top-7 mt-2 w-48 bg-gray-800 rounded-md overflow-hidden z-10 hidden">
                        {data.subcat.map(el => (
                            <a href={el.slug} class="block px-4 py-2 text-sm text-gray-200 hover:bg-gray-700">
                                {el.title}
                            </a>
                        ))}
                    </div>
                </li>
        )}
        else {
            return (
                    <li class="mb-4 pl-2 lg:mb-0 lg:pl-0 lg:pr-1" data-te-nav-item-ref>
                        <a
                        class="p-0 text-sm transition duration-200  hover:ease-in-out   motion-reduce:transition-none lg:px-2"
                        href={`${data.slug}`}
                        data-te-nav-link-ref
                        >
                            {data.title}
                        </a>
                    </li>
                )
            }
        })}
    </ul>
</nav>
const dropdownToggle = document.querySelectorAll('.dropdown');
const dropdownMenu = document.querySelectorAll('.dropdowns > div');


    dropdownToggle.forEach(button => button.addEventListener('click', () => {
            dropdownMenu.forEach((el) => {
                if(button.getAttribute('data-title') === el.getAttribute('data-title')){
                    el.classList.toggle('hidden')
                }
            });     
    }))

我尝试使用focus-within,但没有按预期工作

.dropdowns:focus-within > div {
    display: block;
}

我认为这个解决方案:

window.onclick = function(event) {
  if (!event.target.matches('.dropbtn')) {
    var dropdowns = document.getElementsByClassName("dropdown-content");
    var i;
    for (i = 0; i < dropdowns.length; i++) {
      var openDropdown = dropdowns[i];
      if (openDropdown.classList.contains('show')) {
        openDropdown.classList.remove('show');
      }
    }
  }
}

对性能不利

JavaScript CSS 菜单 下拉 Astro

评论

0赞 Pointy 8/23/2023
在删除类之前,您不必执行测试。.contains()
0赞 Pointy 8/23/2023
此外,除非您的页面有数千个下拉结构,否则这不应该是任何类型的性能问题。
0赞 nicoiscoding 8/23/2023
@Pointy window.onclick 解决方案,我在 Google 上找到了它,但我认为它对性能不利,因为它每次点击时都会执行代码,但是我该如何为我的代码实现它?
0赞 Pointy 8/23/2023
你真的注意到性能问题了吗?
1赞 James 8/23/2023
在 dropdownToggle.foreach 中,您可以通过 button.parentNode(即 button.parentNode.classList.toggle('hidden'))获取父“dropdowns”元素。无需搜索匹配的数据标题(除非显示,否则可能根本不需要数据标题)

答:

0赞 Lajos Arpad 8/24/2023 #1

这对性能来说还不错,因为您需要一个点击处理程序,每当要关闭下拉列表时,它都会注意到。你不应该优化那些不慢的东西,因为你有可能用一些难以维护的东西使你的代码过于复杂,而且可能也更慢。

但是,如果您确实注意到一些性能问题(例如,当您有数千个下拉列表时),那么您可以简单地创建一些资源,为了简单起见,让我们调用:currentDropdown

  • 每当发生单击时,如果之前设置了该下拉列表,并且该下拉列表与您刚刚打开的下拉列表不匹配(如果您打开了下拉列表),则将其折叠currentDropdown
  • 每当打开下拉菜单时,只需将其设置为它currentDropdown
  • 每当单击发生在任何下拉列表之外时,请确保不要忘记设置为currentDropdownundefined
  • 如果需要在选择下拉列表中的项目后关闭下拉列表,则还要设置为currentDropdownundefined

因此,您最多只能处理一个下拉列表,而不是循环所有下拉列表。

如果你真的想避免在下拉列表没有打开的情况下在下拉列表之外出现点击事件(但我不建议这样做),那么你可以在任何下拉列表之外单击时,这样下拉菜单之外的点击就不会在你不需要的时候触发事件侦听器。但是,同样,我不建议这样做,因为它是代码复杂性,几乎没有任何收益。避免如上所述的循环就足够了。removeEventListener