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

当前遇到的问题
尝试用overflow-y: auto实现垂直滚动,但存在两个问题:
- 即便设置了
overflow-x: visible,水平方向溢出的子菜单仍被隐藏 - 浏览器默认的滚动条样式不符合需求
当前实现代码(最小工作示例)
优先纯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
相关产品推荐
相关产品推荐

