Handsontable 列菜单(Column Menu)插件完整实战指南:配置、过滤项与键盘导航
2026/9/20 19:33:32 网站建设 项目流程

Handsontable 列菜单(Column Menu)插件完整实战指南:配置、过滤项与键盘导航

【免费下载链接】handsontableJavaScript Data Grid / Data Table with a Spreadsheet Look & Feel. Works with React, Angular, and Vue. Supported by the Handsontable team ⚡项目地址: https://gitcode.com/gh_mirrors/ha/handsontable

本指南以 Handsontable 的DropdownMenu插件为主线,讲解如何在表格列头添加可配置的下拉菜单:从dropdownMenu: true一行启用,到用条目数组定制菜单内容,再到借助Filters插件在菜单内构建过滤界面。读完本文,你将掌握列菜单的完整配置方式、与右键 Context Menu 的协作边界、菜单条目的动态构建机制,以及面向键盘和读屏用户的完整导航方案。

概述:列菜单是什么

DropdownMenu插件为表格的列头添加一个可配置的下拉菜单。它的典型触发方式是点击列头中显示的菜单按钮(一个向下箭头样式的按钮),而不是像上下文菜单那样右键点击。

列菜单与右键上下文菜单(Context Menu)是两个相互独立的插件,各有各的配置键:

插件配置键触发方式
列菜单dropdownMenu点击列头按钮,或按快捷键Shift+Alt+
上下文菜单contextMenu右键点击单元格 / 行头 / 列头

注意dropdownMenucontextMenu是分开配置的。设置其中一个不会影响另一个——尽管两者接受的菜单条目使用完全相同的格式。

从源码结构看,两个插件共享同一套菜单基础设施:DropdownMenu基于contextMenu目录下的共享Menu类构建,复用ItemsFactory(条目工厂)和CommandExecutor(命令执行器),但菜单的触发、作用范围和钩子前缀(beforeDropdownMenu*/afterDropdownMenu*)都是独立的。这一点在 DropdownMenu 插件说明 中有明确记录。

快速启用:一行配置开启列菜单

要启用插件,只需在初始化 Handsontable 时将dropdownMenu配置选项设为true。下面的示例来自官方文档的 Quick setup 演示(JavaScript 版与 TypeScript 版):

import Handsontable from 'handsontable/base'; import { registerAllModules } from 'handsontable/registry'; // Register all Handsontable's modules. registerAllModules(); const container = document.querySelector('#example1'); new Handsontable(container, { data: [ ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1'], ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2'], ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3'], ], colHeaders: true, dropdownMenu: true, height: 'auto', autoWrapRow: true, autoWrapCol: true, licenseKey: 'non-commercial-and-evaluation', });

注意两个前提条件:

  • 必须启用列头colHeaders: true是必需的,因为菜单按钮被注入到列头单元格(TH)中;
  • 必须注册模块:通过registerAllModules()注册全部模块,否则插件不会被加载。如果使用按需注册,需要显式注册DropdownMenu插件。

在 React 包装器中(example1.jsx),配置以 JSX 属性的形式传入:

import { HotTable } from '@handsontable/react-wrapper'; import { registerAllModules } from 'handsontable/registry'; registerAllModules(); const ExampleComponent = () => { return ( <HotTable data={[ ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1'], ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2'], ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3'], ]} colHeaders={true} dropdownMenu={true} height="auto" autoWrapRow={true} autoWrapCol={true} licenseKey="non-commercial-and-evaluation" /> ); };

Angular 包装器(example1.ts)通过GridSettings对象传入配置:

readonly hotSettings: GridSettings = { colHeaders: true, dropdownMenu: true, height: 'auto', autoWrapRow: true, autoWrapCol: true, };

Vue 3 包装器(example1.vue)则使用ref<GridSettings>保存配置:

<script setup lang="ts"> import { ref } from 'vue'; import { HotTable } from '@handsontable/vue3'; import { registerAllModules } from 'handsontable/registry'; import type { GridSettings } from 'handsontable/settings'; registerAllModules(); const hotSettings = ref<GridSettings>({ data: [ ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1'], ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2'], ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3'], ], colHeaders: true, dropdownMenu: true, height: 'auto', autoWrapRow: true, autoWrapCol: true, licenseKey: 'non-commercial-and-evaluation', }); </script> <template> <div id="example1"> <HotTable :settings="hotSettings" /> </div> </template>

插件配置:使用默认菜单还是自定义菜单

dropdownMenu选项接受两种取值:

取值效果
true启用插件,并使用默认的菜单条目列表
字符串数组启用插件,并使用你指定的自定义条目列表
false(或省略)完全禁用插件

默认菜单内容

当设为true时,菜单使用插件源码中定义的默认条目顺序。从 dropdownMenu.ts 的DEFAULT_ITEMS静态属性可以看到实际顺序:

col_left → col_right → 分隔线 → remove_col → 分隔线 → clear_column → 分隔线 → make_read_only → 分隔线 → alignment

其中:

  • col_left/col_right:在左侧 / 右侧插入一列;
  • remove_col:删除选中列;
  • clear_column:清空选中列的数据;
  • make_read_only:将选中单元格设为只读;
  • alignment:单元格文本对齐(子菜单包含左 / 中 / 右 / 上 / 下 / 两端对齐等)。

自定义菜单:使用条目数组

dropdownMenu设为一个字符串数组即可自定义条目。下面的示例来自文档的 Plugin configuration 演示(example2.js):

import Handsontable from 'handsontable/base'; import { registerAllModules } from 'handsontable/registry'; registerAllModules(); const container = document.querySelector('#example2'); new Handsontable(container, { data: [ ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1'], ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2'], ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3'], ], colHeaders: true, licenseKey: 'non-commercial-and-evaluation', height: 'auto', dropdownMenu: ['remove_col', '---------', 'make_read_only', '---------', 'alignment'], autoWrapRow: true, autoWrapCol: true, });

这里'---------'表示一条分隔线。自定义数组的意义在于:手动列出条目后,其他插件就不会再往菜单里追加条目了——这正好解决了“想排除某插件条目”的需求。

全部预定义条目参考

除上述默认条目外,列菜单还支持以下预定义条目(与上下文菜单共享同一套条目格式,完整列表见 Context Menu 指南)。注意:许多条目来自其他插件,只有对应插件启用时才生效:

作用依赖插件
row_above/row_below在上方 / 下方插入一行
col_left/col_right在左侧 / 右侧插入一列
---------添加分隔线
remove_col/remove_row删除选中列 / 行
clear_column删除选中列的数据
undo/redo撤销 / 重做上一次操作UndoRedo
make_read_only将选中单元格设为只读
alignment对齐单元格文本
cut/copy剪切 / 复制选中单元格到剪贴板CopyPaste
copy_with_column_headers复制单元格及其最近的列头CopyPaste(需copyColumnHeaders: true
copy_with_column_group_headers复制单元格及其所有相关列头NestedHeaders+CopyPaste
copy_column_headers_only仅复制最近的列头CopyPaste(需copyColumnHeadersOnly: true
freeze_column/unfreeze_column冻结 / 取消冻结选中列ManualColumnFreeze
borders为选中单元格添加边框CustomBorders
commentsAddEdit/commentsRemove/commentsReadOnly添加编辑 / 删除 / 只读化批注Comments
mergeCells合并或取消合并选中单元格MergeCells
add_child/detach_from_parent插入子行 / 从父行分离NestedRows
hidden_columns_hide/hidden_columns_show隐藏 / 显示选中列HiddenColumns
hidden_rows_hide/hidden_rows_show隐藏 / 显示选中行HiddenRows
filter_by_condition等 5 个条目在菜单内构建过滤界面Filters(详见下文)
export_file打开“导出”子菜单(CSV / Excel)ExportFile(Excel 项在未配置 XLSX 引擎时隐藏)

freeze_column/unfreeze_column为例:它们来自ManualColumnFreeze插件。启用该插件后,两个条目会自动加入默认列菜单,并且各自只在适用时显示——freeze_column只在列未冻结时出现,unfreeze_column只在列已冻结时出现。在 DropdownMenu 插件的源码 中,freeze_columnunfreeze_column等键均通过contextMenu/predefinedItems目录下的命令实现注册。

条目选项格式

如果你要传入完整对象形式的条目(而不是字符串键),每个条目支持以下选项(详见 Context Menu 指南):

选项说明
key条目唯一标识。顶层条目用'row_above'这样的键;子菜单条目必须用父键:子键格式(如'colors:red'
name菜单中显示的标签。可以是字符串或返回字符串的函数,支持 HTML;函数中this指向 Handsontable 实例
disabled条目置灰不可点。布尔值或返回布尔值的函数
hidden是否从菜单中完全隐藏条目。布尔值或返回布尔值的函数
checked是否显示勾选标记。同时让条目对辅助技术表现为复选框
callback点击条目时执行的回调,接收keyselectionclickEvent三个参数
submenu定义嵌套子菜单,接收包含items数组的对象
renderer自定义条目 HTML 渲染函数,必须返回HTMLElement
disableSelectiontrue时悬停条目不高亮
isCommandfalse时点击条目不执行命令、不关闭菜单

菜单条目是每次打开时重建的

列菜单的条目列表在每次打开菜单时都会重新构建,因此条目会跟随当前配置实时变化。这也是为什么启用/禁用其他插件(如ManualColumnFreeze)后,菜单内容能立即反映出来。

  • 如果你想排除某个插件的条目,就手动列出想要的条目(把dropdownMenu设为数组),而不是设成true
  • 如果你想每次打开时动态修改条目列表,请使用beforeDropdownMenuSetItems钩子。

从源码看,prepareMenuItems()方法在每次打开时依次执行两条钩子链(dropdownMenu.ts):

  1. afterDropdownMenuDefaultOptions——允许其他插件往默认选项里追加条目(Filters插件正是通过这个钩子把过滤条目加进来的);
  2. beforeDropdownMenuSetItems——允许在最终渲染前对完整条目列表做最后一次修改。

构建完成后,所有条目会注册到CommandExecutor中,供点击或编程式调用。

过滤菜单条目:把筛选界面放进列菜单

Filters插件启用时,它会向列菜单追加以下条目,这些条目共同构建完整的过滤界面。这些条目只在列菜单中可用,不会出现在右键上下文菜单中

作用
filter_by_condition添加第一个过滤条件
filter_by_condition2添加第二个过滤条件。第二个条件的下拉选择框依赖它
filter_operators选择连接两个条件的运算符(AndOr
filter_by_value选择要保留的值
filter_action_barOKCancel按钮应用或取消过滤

从 filters.ts 的源码可以看到,这 5 个条目以固定顺序注册,并在afterDropdownMenuDefaultOptions钩子中被注入到列菜单:

['filter_by_condition', null], ['filter_operators', null], ['filter_by_condition2', null], ['filter_by_value', null], ['filter_action_bar', null]

完整的过滤配置指南见 Column filter(列过滤)指南;过滤菜单内的键盘与指针操作行为见其中的 Navigate the filter menu 一节,例如:

  • Tab/Shift+Tab在搜索输入框、Select allClear all、条件控件和操作栏控件之间移动焦点;
  • 搜索输入框聚焦时,/Filter by value列表中移动;
  • Select all/Clear all聚焦时,EnterSpace执行对应动作;
  • 悬停非过滤条目(如Clear column)后,过滤组件的焦点顺序会重置,下一次Tab会聚焦到第一个过滤组件。

导航列菜单:纯键盘操作

打开列菜单后,可以使用以下键盘快捷键导航:

  • /:在菜单条目之间移动;
  • :在从左到右(LTR)布局中打开子菜单;关闭子菜单并返回父菜单;
  • :在从右到左(RTL)布局中打开子菜单;关闭子菜单并返回父菜单;
  • Home(Windows 下Ctrl+,macOS 下Cmd+):移动到第一个可用条目;
  • End(Windows 下Ctrl+,macOS 下Cmd+):移动到最后一个可用条目;
  • Page Up/Page Down:按一个可见菜单页移动;
  • Enter/Space:执行选中条目,或打开其子菜单;
  • Escape:关闭列菜单或当前子菜单。

这些快捷键行为在 dropdownMenu 插件的键盘快捷键测试 目录中有对应的测试用例(如arrowUp.spec.jsescape.spec.jsenterOrSpace.spec.jspageUp.spec.js等)。

菜单内的过滤控件遵循额外的导航规则,详见 Navigate the filter menu。

相关键盘快捷键

以下快捷键用于打开列菜单:

WindowsmacOS作用ExcelSheets
Shift+Alt+++打开列菜单。可在任意单元格中使用,前提是该列列头显示了菜单按钮
Ctrl+Enter+Enter打开列菜单。仅在聚焦到带菜单按钮的列头时生效

要点:

  • Shift+Alt+数据单元格即可触发;Ctrl/+Enter则要求列头已聚焦
  • 要让箭头键把焦点移到列头上,需要启用navigableHeaders: true选项;
  • 更多键盘导航细节见 Accessibility(无障碍)指南的 Keyboard navigation 一节。

从 dropdownMenu.ts 的registerShortcuts()可以看到,这两个快捷键在grid快捷键上下文中注册,Control/Meta+Enter带有captureCtrl: true,且均带有runOnlyIf守卫(例如菜单已打开时不响应)。

源码视角:列菜单的内部机制

结合 dropdownMenu.ts 的源码实现,可以更深入地理解列菜单的工作方式:

插件生命周期

  • PLUGIN_PRIORITY = 230:插件初始化优先级;PLUGIN_DEPS声明依赖AutoColumnSize插件;
  • isEnabled():检查hot.getSettings()['dropdownMenu']是否为真;
  • updatePlugin():在updateSettings()修改dropdownMenu选项时被调用,通过先disablePlugin()enablePlugin()完成热更新;
  • enablePlugin():注册鼠标事件、视图滚动拦截(防止点击按钮时视口发生跳动)、对话框打开时自动关闭菜单,并创建共享的Menu实例(className: 'htDropdownMenu')。

菜单按钮的注入

按钮注入发生在afterGetColHeader钩子中(dropdownMenu.ts):插件会在最底层列头(isBottomMostColumnHeader)的TH内插入一个type="button"tabIndex = -1的按钮元素。启用ariaTags时,还会为按钮设置aria-hidden和为TH设置aria-haspopup="menu",保证读屏用户能感知到菜单的存在。

菜单的打开流程

open()方法(dropdownMenu.ts)的调用顺序是:

  1. 触发beforeDropdownMenuShow钩子(此时菜单状态尚未提交,若监听器调用updateSettings({ dropdownMenu })会同步重建插件,后续流程基于新实例继续);
  2. 调用prepareMenuItems()重建条目列表;
  3. menu.open()打开菜单;
  4. 设置位置偏移并定位。

打开后触发afterDropdownMenuShow,关闭后触发afterDropdownMenuHide,执行命令时触发afterDropdownMenuExecute——这 5 个钩子都在插件源码第 26-30 行统一注册。

编程式控制

hot.getPlugin('dropdownMenu')拿到插件实例后,可以调用:

const menu = hot.getPlugin('dropdownMenu'); hot.selectCell(0, 0); menu.open({ top: 50, left: 50 }); // 打开菜单并定位 menu.close(); // 关闭菜单 menu.executeCommand('remove_col'); // 执行命令

executeCommand()支持所有预定义命令,如'col_left''col_right''clear_column''remove_col''undo''redo''make_read_only''alignment:left''alignment:top''alignment:right''alignment:bottom''alignment:middle''alignment:center',也可以执行自定义注册的命令。注意:executeCommand()仅在选中了单元格时生效,没有选中时调用不做任何事。

列菜单相关的钩子(Hooks)

列菜单专属的钩子共有 5 个,均以DropdownMenu插件实例为参数:

钩子触发时机
afterDropdownMenuDefaultOptions构建默认选项之后、条目列表确定之前;适合向菜单追加自定义条目
beforeDropdownMenuShow菜单显示之前
afterDropdownMenuShow菜单显示之后
beforeDropdownMenuSetItems条目列表最终确定之前;适合按需增删条目
afterDropdownMenuHide菜单隐藏之后

一个典型用法是监听beforeDropdownMenuSetItems,在每次菜单打开前按列号动态调整条目(例如对特定列禁用某些操作)。

相关资源

相关指南

  • Context menu(上下文菜单)——与列菜单共享条目格式与菜单基础设施的姊妹插件
  • Column filter(列过滤)——列菜单内过滤界面的完整使用指南

配置选项

  • dropdownMenu——本文主角,见 API 参考

源码参考

  • DropdownMenu 插件实现——插件完整源码(含默认条目、快捷键、按钮注入)
  • DropdownMenu 插件说明——插件设计约定
  • 预定义菜单条目——col_leftremove_colalignment等命令的实现
  • Filters 插件实现——过滤条目的注册与过滤界面组件
  • 插件 API 索引——全部插件与选项的入口

【免费下载链接】handsontableJavaScript Data Grid / Data Table with a Spreadsheet Look & Feel. Works with React, Angular, and Vue. Supported by the Handsontable team ⚡项目地址: https://gitcode.com/gh_mirrors/ha/handsontable

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询