QMenu 组件是一个方便展示菜单的方式。它可以很好地与 QList 作为下拉内容搭配使用,但不仅限于此。
用法
QMenu 的设计理念是将其作为触发器 DOM 元素/组件的直接子元素放置。不用担心 QMenu 内容会从容器继承 CSS,因为 QMenu 会通过 Quasar Portal 作为 <body> 的直接子元素注入。
如果您希望菜单可以自动关闭,不要忘记给可点击的菜单项加上 v-close-popup 指令。另外,也可以使用 QMenu 的 auto-close 属性,或者自行通过 v-model 来处理关闭操作。
基础
如果您想要有条件地激活或停用 QMenu,请使用 v-if 而不是 v-show。
悬停打开 v2.26+
使用 hover 属性后,当鼠标悬停在目标元素上时菜单会自动打开,当指针离开目标和菜单后菜单关闭。hover-hide-delay 属性控制指针在两者之间移动(或返回)时菜单延迟关闭的宽限期,hover-delay 则延迟打开。
点击/轻触和键盘交互仍然照常切换菜单,因此触屏设备(没有悬停事件)会自然降级为点击交互;这也意味着当菜单因悬停而打开时,点击目标(或用 Enter 激活目标)会关闭它。唯一的例外是:在菜单还处于打开动画过程中落下的点击会被忽略——这样一来,在目标上「移动并点击」这一连贯手势就不会把刚刚由同一手势打开的菜单又关掉。悬停打开的菜单不会将键盘焦点移入菜单内部。子菜单同样适用:从父菜单悬停到由它打开的子菜单时,整个菜单链保持打开。
hover 和 context-menu 属性互斥;同时设置时,context-menu 优先生效,hover 无效。此外,悬停触发的打开会忽略 touch-position,该属性仅对点击/轻触打开生效。
子菜单
尺寸和样式
上下文菜单
您还可以将 QMenu 作为上下文菜单。右键点击父目标即可触发,触屏设备上长按同样适用。上下文菜单仅通过这些交互方式打开,因此 hover 属性对其无效。
持久化
如果您希望在应用路由变化、按下 ESCAPE 键或点击/触摸 QMenu 外部时不要关闭菜单,那么请使用 persistent 属性:
过渡
在下面的示例中,展示了一些过渡效果。有关可用过渡的完整列表,请转到 过渡效果 页面。
可复用
下面的示例展示了如何创建一个可在不同目标容器中复用的菜单。
定位
QMenu 的位置可以自定义。它会考虑 anchor 和 self 这两个可选属性。QMenu 弹出框的最终位置是计算得出的,以便使其显示在可用的屏幕空间上:当请求的摆放位置放不下时,若锚点在该轴上的相反一侧空间更充裕,弹出层就会翻转到相反一侧,否则仍停留在请求的一侧(因此当触发器上下空间相等时,会保留你设置的 anchor/self),并被限制在它所拥有的空间内。
对于水平定位,您可以使用 start 和 end 来自动适应 RTL 或非 RTL。start 和 end 在非 RTL 中表示"左",在 RTL 中表示"右"。
<q-menu hover anchor="bottom left" self="top left">
<q-item clickable>
<q-item-section>New tab</q-item-section>
</q-item>
<q-item clickable>
<q-item-section>New incognito tab</q-item-section>
</q-item>
</q-menu>offset 属性并不会把弹出层按指定的像素数「平移」。它是把锚点元素的边界框向外撑开:offset[0] 让边界框的 left(左)边向左移、right(右)边向右移,而 offset[1] 让 top(上)边向上移、bottom(下)边向下移。随后弹出层的 self 点会对齐到撑开后边界框的 anchor 点,最后再把结果限制在可用的屏幕空间内。
有两个后果值得了解,因为它们都会让 offset 看起来像是在某个轴向上被忽略了:
middle或center锚点不会随 offset 移动。 两边被撑开相同的量后,中点仍停留在原处,所以无论传入什么值,offset[0]对anchor="... middle"都是空操作(no-op),offset[1]对anchor="center ..."也是空操作。QMenu 默认的anchor(bottom start)在两个轴向上都会响应,但你自己设置的middle/center锚点则不会。- 被裁剪的弹出层同样不会随 offset 移动。 由于 offset 是把边界框向外推,把 QMenu 锚定在全宽或紧贴屏幕边缘的元素上(或传入一个非常大的值),可能会把弹出层推出视口边缘,在那里它会被裁剪回来,最终位置也就不再取决于 offset 值了。请把 QMenu 挂载到 inline 或
inline-block触发器上,并让anchor/self指向有自由空间的方向,这样 offset 才有发挥的余地。
cover(覆盖)属性会完全忽略 offset,因为此时弹出层本就应当正好盖在锚点元素之上。
无障碍访问 v2.25+
语义
QMenu 渲染为一个普通的定位容器,刻意不声明任何 ARIA role:它可以承载任何类型的内容,而 WAI-ARIA menu role 只允许菜单项作为子元素——表单、日期选择器或列表放在其下都会导致标记无效。
所以当弹出内容确实是一个菜单——一组命令时——请通过在包裹的 QList 上设置 role="menu" 来声明。其中的 QItem 会自动适配:可操作的条目(clickable 或链接条目,包括禁用的)将自身暴露为 menuitem,其他内容(节标题等)保持中性,QSeparator 已经自带 separator 语义。单个条目可以覆盖其派生的 role——例如用 role="menuitemcheckbox" 或 role="menuitemradio" 用于切换条目,此时管理 aria-checked 就是你的责任了。上方的"基础"示例展示了声明方式。
如果你将 role 附加到 QMenu 容器本身(它会透传你传入的任何 role),请确保其全部内容都满足该 role 的要求。
锚点语义
锚点是你自己的标记,但它是打开弹出层的控件,所以 QMenu 会从外部维护触发器的 ARIA:aria-expanded 跟随打开状态,QMenu 容器上声明的 role 会镜像到锚点上作为 aria-haspopup(该属性命名弹出层的 role,所以只有 menu、listbox、tree、grid 和 dialog 可以被镜像)。因此包裹 QMenu 的 <q-btn> 会被播报为一个折叠或展开的触发器,无需额外工作。
这要求锚点是 ARIA 允许设置该状态的元素——<button>、带 href 的链接,或声明了小部件 role(如 role="button")的任何元素。普通 <div> 计算为通用 role,aria-expanded 在其上是无效的,所以 QMenu 不会触碰它:在将这样的锚点用作触发器之前,请给它一个合适的 role(以及键盘激活路径)。另外两种情况也被刻意不处理——context-menu(上下文菜单)弹出层(通过右键或长按打开而非激活),以及你自行在锚点上设置的 aria-expanded/aria-haspopup(QMenu 永远不会覆盖它们)。
请注意,当 role 放在弹出层内部的 QList 上——即上文推荐的形式——QMenu 看不到它,所以请自行在锚点上添加 aria-haspopup="menu":
<q-btn label="操作" aria-haspopup="menu">
<q-menu>
<q-list role="menu">
<!-- ... -->
</q-list>
</q-menu>
</q-btn>键盘导航
由于菜单渲染在页面末尾附近,如果 Tab 越过其最后一个可聚焦元素(或 Shift + Tab 越过第一个),键盘焦点会脱离页面。遵循 WAI-ARIA APG,菜单会关闭并将焦点从锚点继续,就像 Escape 关闭菜单并将焦点返回锚点一样。菜单 内部 多个可聚焦元素之间的 Tab 导航照常工作,persistent(持久性)菜单也会取消此关闭行为。
注意可聚焦的菜单项是普通的 Tab 焦点——QMenu 目前还不提供 APG 菜单模式 中为 role="menu" 内容描述的方向键导航。