Vanilla Javascript 切换下拉菜单

2024-01-04

我的大脑已经检查过了周末......

我正在寻找一种纯 Javascript 解决方案,如果在单击另一个主菜单项时打开一个下拉菜单框,则先前打开的下拉菜单将关闭,然后显示新单击的主菜单项的下拉菜单。我知道这可能很简单,但我无法想出一个不复杂的解决方案。

此外,如果您单击菜单项外部(文档中不是菜单项或下拉框的任何位置),则应关闭所有打开的下拉菜单。

感谢您的任何帮助。

function testFunc(el) {
  var parent = el.parentElement;
  var dd = parent.lastChild.previousSibling;
  dd.classList.toggle('show');
}
ul { list-style: none; margin: 0; padding: 0; }
ul li {
  width: 100px;
  float: left;
  background: #dbdbdb;
  line-height: 2em;
  text-align: center;
  margin: 0 5px;
  cursor: pointer;
}
ul li span {
  display: block;
}
ul li ul {
  display: none;
}

.show {
  display: block;
}
<ul>
  <li>
    <span onclick="testFunc(this)">Item 1</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span onclick="testFunc(this)">Item 2</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span onclick="testFunc(this)">Item 3</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span onclick="testFunc(this)">Item 4</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
</ul>

切换菜单可见性

您可以将上次打开的菜单保存在变量中opened函数之外。然后当点击菜单时如果opened is not null它将切换opened(即隐藏上次打开的菜单)并切换单击的项目。

let opened = null

function testFunc(el) {

  // gets the <ul> element of the clicked menu item
  const menu = el.parentElement.lastChild.previousSibling;

  if (!opened) {

    // no menu item is shown
    opened = menu
    opened.classList.toggle('show');

  } else if (menu == opened) {

    // the clicked item is already showing
    menu.classList.toggle('show')
    opened = null

  } else {

    // the clicked item is hiddden but another one is showing
    opened.classList.toggle('show')
    opened = menu
    opened.classList.toggle('show')

  }

}

这是代码:

let opened = null

function testFunc(el) {

  const menu =  el.parentElement.lastChild.previousSibling;
  
  if(!opened) {
    opened = menu
    opened.classList.toggle('show');
  } else if(menu == opened) {
    menu.classList.toggle('show')
    opened = null
  } else {
    opened.classList.toggle('show')
    opened = menu
    opened.classList.toggle('show')
  }
  
}
ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

ul li {
  width: 100px;
  float: left;
  background: #dbdbdb;
  line-height: 2em;
  text-align: center;
  margin: 0 5px;
  cursor: pointer;
}

ul li span {
  display: block;
}

ul li ul {
  display: none;
}

.show {
  display: block;
}
<ul>
  <li>
    <span onclick="testFunc(this)">Item 1</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span onclick="testFunc(this)">Item 2</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span onclick="testFunc(this)">Item 3</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span onclick="testFunc(this)">Item 4</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
</ul>

ES6 语法的变体

这是带有一些 ES6 语法的变体,请注意,我更改了 HTML 命名结构以更好地维护代码,通过类名调用元素允许

  • 不必使用内联事件侦听器

  • 在一行中调用所有菜单项

这是 JavaScript 代码:

let opened = null
const toggleVisibility = e => e.classList.toggle('show')

const toggleDropDown = e => {

  const clickedItem = e.target.parentElement.lastChild.previousSibling

  toggleVisibility(clickedItem);

  if (!opened) {
    opened = clickedItem
  } else if (opened == clickedItem) {
    opened = null
  } else {
    toggleVisibility(opened);
    opened = clickedItem
  }

}

[...document.querySelectorAll('.dropDown')].forEach(dropDown => dropDown.addEventListener('click', toggleDropDown))
let opened = null
const toggleVisibility = e => e.classList.toggle('show')

const toggleDropDown = e => {

  const clickedItem = e.target.parentElement.lastChild.previousSibling

  toggleVisibility(clickedItem);

  if (!opened) {
    opened = clickedItem
  } else if (opened == clickedItem) {
    opened = null
  } else {
    toggleVisibility(opened);
    opened = clickedItem
  }

}

[...document.querySelectorAll('.dropDown')].forEach(dropDown => dropDown.addEventListener('click', toggleDropDown))
ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

ul li {
  width: 100px;
  float: left;
  background: #dbdbdb;
  line-height: 2em;
  text-align: center;
  margin: 0 5px;
  cursor: pointer;
}

ul li span {
  display: block;
}

ul li ul {
  display: none;
}

.show {
  display: block;
}
<ul>
  <li>
    <span class="dropDown">Item 1</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span class="dropDown">Item 2</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span class="dropDown">Item 3</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span class="dropDown">Item 4</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
</ul>

切换菜单可见性+单击其他地方时关闭

如果您想在用户单击菜单外部时关闭任何打开的菜单,您需要在文档本身上有一个事件侦听器。因此,您不必为每个菜单按钮配备一个事件侦听器,而是让一个事件侦听器监视文档中发生的任何点击。

事件侦听器将确定单击的项目是否是菜单按钮,在这种情况下,它将运行菜单处理程序。否则它将关闭最后打开的菜单项。

JavaScript 代码:

let opened = null
const toggleVisibility = e => e.classList.toggle('show')

const handleDropdown = e => {

  const clickedItem = e.parentElement.lastChild.previousSibling

  toggleVisibility(clickedItem)

  if (!opened) {
    opened = clickedItem
  } else if (opened == clickedItem) {
    opened = null
  } else {
    toggleVisibility(opened)
    opened = clickedItem
  }

}

const handleClick = e => {

  if (e.target.className.includes('dropDown')) {
    handleDropdown(e.target)
  } else if (opened) {
    toggleVisibility(opened)
    opened = null
  }

}

document.addEventListener('click', handleClick)

这是完整的代码:

let opened = null
const toggleVisibility = e => e.classList.toggle('show')

const handleDropdown = e => {

  const clickedItem = e.parentElement.lastChild.previousSibling

  toggleVisibility(clickedItem)

  if (!opened) {
    opened = clickedItem
  } else if (opened == clickedItem) {
    opened = null
  } else {
    toggleVisibility(opened)
    opened = clickedItem
  }

}

const handleClick = e => {

  if (e.target.className.includes('dropDown')) {
    handleDropdown(e.target)
  } else if (opened) {
    toggleVisibility(opened)
    opened = null
  }

}

document.addEventListener('click', handleClick)
ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

ul li {
  width: 100px;
  float: left;
  background: #dbdbdb;
  line-height: 2em;
  text-align: center;
  margin: 0 5px;
  cursor: pointer;
}

ul li span {
  display: block;
}

ul li ul {
  display: none;
}

.show {
  display: block;
}
<ul>
  <li>
    <span class="dropDown">Item 1</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span class="dropDown">Item 2</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span class="dropDown">Item 3</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span class="dropDown">Item 4</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
</ul>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Vanilla Javascript 切换下拉菜单 的相关文章

  • ReferenceError:regeneratorRuntime未定义(但在范围内工作)

    我遇到过这种奇怪的情况 ReferenceError regeneratorRuntime is not defined 我已经设法在一个非常小的设置中重现 与同一问题上的类似问题相比 并且还注意到一些奇怪的行为 具体取决于是否使用范围 以
  • 如何使用 JavaScript 创建链接?

    我有一个标题字符串和一个链接字符串 我不知道如何将两者放在一起以使用 JavaScript 在页面上创建链接 任何帮助表示赞赏 我试图解决这个问题的原因是因为我有一个 RSS 源并且有一个标题和 URL 列表 我想将标题链接到 URL 以使
  • Javascript 函数查找数字的倍数

    创建一个名为的函数multiplesOf 它将接受两个参数 第一个参数是数字数组 第二个参数是数字 该函数应返回一个新数组 该数组由参数数组中的每个数字组成 该数字是参数数字的倍数 So multiplesOf 5 6 7 8 9 10 3
  • 在 contenteditable div 中选择范围

    我有一个contenteditablediv 和其中的一些段落 这是我的代码 div style border solid 1px black width 300px height 300px div Hello world div div
  • socket.io 的良好初学者教程? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 是否存在 IsCallable 为 false 但 IsConstructor 为 true 的 JS 对象?

    ECMAScript 规范函数可调用 https www ecma international org ecma 262 6 0 index html sec iscallable当且仅当其参数具有 Call 内部方法时返回 true 它在
  • 以编程方式填写reactjs表单

    我正在编写一个用户脚本 但无法填写由reactjs制作的表单 我的代码 document querySelector id username value email protected cdn cgi l email protection
  • Three.js:缩放几何图形后错误的 BoundingBox

    在我的场景中 我有一个简单的立方体 var test new THREE Mesh new THREE CubeGeometry 10 10 10 new THREE MeshBasicMaterial scene add test 该立方
  • 使用 JavaScript 禁用第三方 cookie

    我正在努力根据所有在欧盟运营的公司的数据保护规则实施新的 Cookie 政策合规性 根据该规则 用户在使用任何网站时必须能够拒绝 接受除必需的 Cookie 之外的所有内容 在我客户的网站中 我可以看到正在存储以下第三方 cookie ga
  • 带有淘汰赛js的隐形recaptcha

    我正在完成隐形验证码 但我在实现它时遇到问题 谷歌开发人员页面中的代码显示它应该是这样的
  • 在 HTML5 画布中,如何用我选择的背景遮盖图像?

    我试图用画布来实现这一点 globalCompositeOperation 但没有运气 所以我在这里问 这里有类似的问题 但我没有在其中找到我的案例 我的画布区域中有图层 从下到上的绘制顺序 画布底座填充纯白色 fff 用fillRect
  • 为什么我们在打字稿中使用 HTMLInputElement ?

    我们为什么使用 document getElementById ipv as HTMLInputElement value 代替 document getElementById ipv value 功能getElementById返回具有类
  • 有没有办法在 onclick 触发时禁用 iPad/iPhone 上的闪烁/闪烁?

    所以我有一个有 onclick 事件的区域 在常规浏览器上单击时 它不会显示任何视觉变化 但在 iPad iPhone 上单击时 它会闪烁 闪烁 有什么办法可以阻止它在 iPad iPhone 上执行此操作吗 这是一个与我正在做的类似的示例
  • 从数据库检查数据的异步解决方案各种循环子句

    我想要做的是异步检查数据库并从中获取结果 在我的应用程序中我试图实现Asynchronously将此步骤解决为 从数据库中检查手机号码JsonArray循环子句的种类 Create JsonArray从结果 打印创建的数组 我学到了足够多的
  • 带参数的事件监听器

    我想将参数传递给 JavaScript 中的事件侦听器 我已经找到了解决方案 但我无法理解它们为什么或如何工作以及为什么其他解决方案不起作用 我有 C C 背景 但是 Javascript 函数的执行有很大不同 您能否帮助我理解以下示例如何
  • 用于交互式图形绘制的轻量级 JavaScript 库? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我有兴趣了解用于绘制交互式图表的最轻量级 javascript 库 我掌握的数据主要是与海洋研究相关的科学数据 我知道一些 jquery
  • 如何用另一个响应替换窗口的 URL 哈希?

    我正在尝试使用替换方法更改哈希 URL document location hash 但它不起作用 function var anchor document location hash this returns me a string va
  • 如何在执行新操作时取消先前操作的执行?

    我有一个动作创建器 它会进行昂贵的计算 并在每次用户输入内容时调度一个动作 基本上是实时更新 但是 如果用户输入多个内容 我不希望之前昂贵的计算完全运行 理想情况下 我希望能够取消执行先前的计算并只执行当前的计算 没有内置功能可以取消Pro
  • Vue.js[vuex] 如何从突变中调度?

    我有一个要应用于 json 对象的过滤器列表 我的突变看起来像这样 const mutations setStars state payload state stars payload this dispatch filter setRev
  • 如何在react-highcharts中使用图表工具提示格式化程序?

    如何使用图表工具提示格式化程序 我正在使用高图表的反应包装器 我有这样的配置 const CHART CONFIG tooltip formatter tooltip gt var s b this x b each this points

随机推荐

  • 检查从每一组中至少选择一个单选按钮(使用 jquery)

    我在表单上动态拉动并显示单选按钮问题 通过 jquery 选项卡分为不同的部分 因此 有很多 2 3 4 5 等单选按钮在一起 它们具有相同的 名称 但 id 不同 当您在各部分之间点击下一步时 我想检查是否为每组至少选择了一个单选按钮 当
  • Angular 4 和 Bootstrap 4 - 合并验证类

    今天我发现我的应用程序中的验证并不总是正确显示 在一个特定的例子中 角度将失败required if
  • 如何将新的 Orderer 组织添加到现有的 Hyperledger Fabric 网络

    我正在尝试向基于 RAFT 的现有订购服务添加一个新的订购者组织 我正在使用first network from fabric samples作为基础网络 在生成加密材料时 我进行了修改 为另外 1 个订购者组织生成加密材料 这crypto
  • 如何在PyQt5中正确异步加载图像?

    我正在尝试弄清楚如何完成异步图像加载正确地 在 PyQt Qlistview 中 我的主要小部件包括Qlistview and a QLineEdit文本框 我有一个演员数据库 我使用以下子类进行查询QAbstractListModel当在
  • 如何限制计算引擎仅接受来自应用程序引擎的请求

    我正在运行我的服务器Google App engine我在哪里使用nodejs我还有所有其他服务 例如mongoDB Elasticsearch and Redis部署于Compute engine 现在 出于安全考虑 在所有数据库实例 M
  • 如何在 TimePickerAndroid 中为反应本机设置最短日期

    我使用react native创建了一个简单的表单library https github com xgfe react native datepicker与开始时间和结束时间 我问自己为什么我可以在ios上毫无问题地设置开始时间和结束时间
  • 无法使我的 Spring Boot 应用程序与 jsp 一起工作

    我的有问题弹簧靴网络应用程序 它用嵌入式码头 它必须是 jetty 而不是 tomcat 并且它使用jsp意见 但似乎没有办法让嵌入式 jetty 与 jsp 视图一起工作 实际上 当我运行 mu 应用程序服务器时 服务器已成功启动 但是当
  • mysql 酒店客房供应情况

    我在Mysql中有一个可用性表如下 id room id int 11 avail date date 对于每个房间 每个可用日期都有一行 可能存在间隙 例如房间 1 可能有 8 月 1 2 3 5 6 13 14 15 的条目 每隔一天就
  • 如何使用 JSBN 加密 Crypto-JS 密钥?

    我在用着JSBN http www cs students stanford edu tjw jsbn 使用公钥 私钥对加密 解密数据 它非常适合文本数据 包括十六进制字符串 我的问题是现在我有二进制数据 特别是加密JS https cod
  • Oracle 中的有效日期检查

    我有一个以 varchar 格式存储的日期值 有效日期或无效日期 是否可以在sql查询中检查日期是否有效 是的 如果你知道格式并且很少使用 plsql 假设您有格式的日期 yyyy mon dd hh24 mi ss create func
  • IE:indexOf 结果“对象不支持此属性或方法”

    我有以下 if 语句 if buyArray indexOf dealWith 0 1 这是在 ie 即 XP 上的 8 中出现 对象不支持此属性或方法 的情况 有人有解决这个问题的方法吗 是的 IEindexOf 您可以实现如下所示的垫片
  • 具有可用 Eclipse Mylyn 支持的 git 分布式问题跟踪器?

    我在用着git用于版本控制 但我目前缺乏一个好的问题 错误 票证跟踪器与 Eclipse Mylyn 集成 我正在寻找的功能 开源实现 以便我可以添加将来需要的功能 优先使用 GPL LGPL MIT 或 BSD 许可证 分散式 问题mus
  • Azure 表存储异常:409 意外冲突?

    我正在使用 WindowsAzure Storage nuget 包版本 9 0 0 Install Package WindowsAzure Storage Version 9 0 0 以下代码 table CreateIfNotExis
  • 如何在 Django 中加载主干的引导模型

    当主干应用程序加载时 对于我的应用程序来说 这意味着页面已加载 我需要初始收集数据 主干文档说 我不明白这一点 这是否意味着我应该使用初始数据渲染页面 例如 collection initial data 我在后端使用 django 那么如
  • 示例无效的 utf8 字符串?

    我正在测试我的一些代码如何处理错误数据 并且我需要一些无效 UTF 8 的字节序列 你能发布一些 最好能解释一下为什么它们不好 你从哪里得到它们吗 In PHP examples array Valid ASCII gt a Valid 2
  • PhoneGap 还是 JqueryMobile? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我在 PhoneGap Cordova 上构建了应用程序 并在此过程中遇到了很多困难 因此我想我的下一个版本将使用 jQuery Mobile
  • Django 'image' 属性没有与之关联的文件

    当用户注册我的应用程序时 当他到达个人资料页面时 我收到此错误 The image attribute has no file associated with it Exception Type ValueError Error durin
  • VS Code 无法识别 Pi Pico SDK 标头

    所以我已经完成了在 Linux 的 Windows 子系统中编译的所有设置 一切都工作正常 我正在 VS Code 中编写代码 但它无法识别 pico stdlib h 等标头 代码编译并工作正常 但最好能正确设置代码 这样我就可以获得自动
  • JavaScript 对象 ID

    JavaScript 对象 变量是否有某种唯一标识符 就像鲁比那样object id 我指的不是 DOM id 属性 而是某种内存地址 如果您想在不修改底层对象的情况下查找 关联具有唯一标识符的对象 您可以使用WeakMap https d
  • Vanilla Javascript 切换下拉菜单

    我的大脑已经检查过了周末 我正在寻找一种纯 Javascript 解决方案 如果在单击另一个主菜单项时打开一个下拉菜单框 则先前打开的下拉菜单将关闭 然后显示新单击的主菜单项的下拉菜单 我知道这可能很简单 但我无法想出一个不复杂的解决方案