跳到正文
Cesirdy

无 JS 的几种实现下拉菜单的方法

代码

前几天让 AI 搓个文章目录的时候才想起来纯 HTML 也能实现这种效果。


一般想到下拉菜单都下意识觉得要用 JS,比如点击按钮后给下拉菜单修改 class 或者是 style。 但如果是比较简单的下拉菜单那倒是不用 JS。

CSS

最简单和常见的思路就是利用 :focus-within 或者是 :hover,不过 :hover 不用点击就能展开对我来说有点反直觉

大概思路就是点击按钮以后让它获得焦点,然后根据这个状态把下面的菜单显示出来。

:focus 也是可以的,不过实际上使用起来会用奇奇怪怪的小问题。
比如点击子元素后会取消按钮的 :focus 状态导致菜单收回。

而 :focus-within 则可以同时对子元素生效。

<div class="menu">
	<button>产品</button>
	<div class="dropdown">
		<a href="#">产品一</a>
		<a href="#">产品二</a>
		<a href="#">产品三</a>
	</div>
</div>
.menu{position:relative}
.dropdown{display:none;position:absolute}
.menu:focus-within .dropdown{display:block}

.dropdown 默认为 display:none。
而当 .menu 里的 <button> 被点击获得焦点后,状态匹配 :focus-within。
因此触发了第三行 CSS,.dropdown 被设置为 display:block。

这种方式严格来说不是根据按钮的点击切换,而是根据焦点状态控制显示。

还有另一种方法是 :checked,逻辑上不太一样。

通过 <label> 和 <input> 绑定,点击 <label> 来给 <input> 转发操作。
当然直接嵌套也是可以的。

<div class="menu">
  <input type="checkbox" id="menu" />
  <label for="menu">产品</label>
  <div class="dropdown">
    <a href="#">产品一</a>
    <a href="#">产品二</a>
    <a href="#">产品三</a>
  </div>
</div>
.menu{position:relative}
.menu input{display:none}
.dropdown{display:none;position:absolute}
.menu input:checked+label+.dropdown{display:block}

.dropdown 默认为 display:none。
而当 <label> 被点击后,将操作转发到 <input> 使 checkbox 的选中状态发生切换,从而匹配 :checked。
因此触发了第四行 CSS,.dropdown 被设置为 display:block。

HTML

更简单和便捷的方法也就是使用 HTML 里的 <details>,自带打开和关闭两个状态,也就直接省去了我们前面想方设法用 CSS 实现的步骤。

<details>
  <summary>点我展开</summary> <!-- 控件 -->
  <p>这里是隐藏的内容。</p>    <!-- 需要隐藏的内容 -->
  <p>这里是更多内容。</p>      <!-- 需要隐藏的内容 -->
</details>

浏览器会自动渲染为:

▶ 点我展开
---
▼ 点我展开
  这里是隐藏的内容。
  这里是更多内容。

<summary> 会自带一个箭头指示,这个箭头可以用 list-style: none 来隐藏。
或者也可以这样来自定义箭头的文字:

summary::marker{content:"▶ "}

<summary> 除了作为按钮的信息文本展示之外,也是整个 <details> 的主要控件。且 <summary> 必须是 <details> 的第一个子元素。

打开和关闭的状态靠 <details> 里的 open 属性,点击 <summary> 会切换这个属性。也可以直接在默认状态下就加入这个属性以让其默认展开。

针对这个属性也可以这样写 CSS:

details[open]{background:#eee}
/* 或者是 */
details:open{background:#eee}

不过自带折叠事件了倒也不太需要用这个选择器了?