You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现可滚动的多级上下文菜单?

问题:构建支持多级滚动的左键触发上下文菜单

需求说明

  • 用HTML+CSS实现左键触发的上下文菜单
  • 支持多级嵌套:悬停带子菜单的菜单项时,旁侧展示子菜单
  • 当菜单项数量过多(如超过30个)时,每一级菜单都需支持滚轮滚动
  • 参考Acrobat PDF的多级上下文菜单效果:

Acrobat PDF中的多级上下文菜单

当前遇到的问题

尝试用overflow-y: auto实现垂直滚动,但存在两个问题:

  1. 即便设置了overflow-x: visible,水平方向溢出的子菜单仍被隐藏
  2. 浏览器默认的滚动条样式不符合需求

当前实现代码(最小工作示例)

优先纯CSS实现,不排斥使用JavaScript

CSS代码

.context-menu {
  position: absolute;
  padding: 0;
  box-sizing: border-box;
  z-index: 2;
}

.context-menu * {
  box-sizing: border-box;
  position: relative;
}

.context-menu ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  background-color: #ccc;
  box-shadow: 0 0 5px #333;
  overflow-y: auto;
  max-height: 500px;
  overflow-x: visible;
}

.context-menu li {
  padding: 3px 5px;
  padding-right: 1.7em;
  cursor: pointer;
  white-space: nowrap;
  text-align: left;
}

.context-menu li:hover {
  background-color: #bbb;
}

.context-menu li .icon_span {
  width: 1.5em;
  height: 1.2em;
  vertical-align: bottom;
  display: inline-block;
  border-right: 1px solid #aaa;
  margin-right: 5px;
  padding-right: 5px;
  text-align: center;
}

.context-menu li .sub_span {
  width: 1em;
  display: inline-block;
  text-align: center;
  position: absolute;
  right: 0.5em;
}

.context-menu li>ul {
  position: absolute;
  top: 0;
  left: 100%;
  opacity: 0;
  transition: opacity 0.2s;
  visibility: hidden;
}

.context-menu li:hover>ul {
  opacity: 1;
  visibility: visible;
}

.context-menu li.separator {
  border-bottom: 1px solid #aaa;
  margin: 0px;
  padding: 0;
  cursor: default;
}

.context-menu li.separator:hover {
  background-color: inherit;
}

.context-menu.orient-left>ul ul {
  left: unset;
  right: 100%;
}

.context-menu.orient-top>ul ul {
  top: unset;
  bottom: 0;
}

.context-menu li[disabled=""] {
  color: #777;
  cursor: default;
}

.context-menu li[disabled=""]:hover {
  background-color: inherit;
}

HTML代码

<div class="context-menu" style="left: 10px; top: 18px;">
  <ul>
    <li> <span class="icon_span"></span><span class="text_span">Option 1</span><span class="sub_span"></span> </li>
    <li> <span class="icon_span"></span><span class="text_span">Option 2</span><span class="sub_span"></span> </li>
    <li> <span class="icon_span"></span><span class="text_span">Option 3</span><span class="sub_span"></span> </li>
    <li class="separator"></li>
    <li> <span class="icon_span"></span><span class="text_span">Menu Option</span><span class="sub_span">›</span>
      <ul>
        <li> <span class="icon_span"></span><span class="text_span">Foo 1</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 2</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 3</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 4</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 5</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 6</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 7</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 8</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 9</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 10</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 11</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 12</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 13</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 14</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 15</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 16</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 17</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 18</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 19</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 20</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 21</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 22</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 23</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 24</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 25</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 26</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 27</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 28</span><span class="sub_span"></span> </li>
        <li> <span class="icon_span"></span><span class="text_span">Foo 29</span><span class="sub_span"></span> </li>
      </ul>
    </li>
    <li class="separator"></li>
    <li> <span class="icon_span"></span><span class="text_span">Option 4</span><span class="sub_span"> </span> </li>
    <li> <span class="icon_span"></span><span class="text_span">Option 5</span><span class="sub_span"> </span> </li>
  </ul>
</div>

内容的提问来源于stack exchange,提问作者Sigmund

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 22:47:03