首页
/ Electron 键盘输入事件(KeyboardInputEvent)详解:通过 webContents.sendInputEvent 程序化注入键盘事件

Electron 键盘输入事件(KeyboardInputEvent)详解:通过 webContents.sendInputEvent 程序化注入键盘事件

2026-09-06 13:17:13作者:宣利权Counsellor

KeyboardInputEvent 是 Electron 中用于描述键盘输入的结构化对象,它是 webContents.sendInputEvent() 的三种合法参数类型之一。理解它的 typekeyCodemodifiers 字段、掌握它与 Accelerator 键码的对应关系,并看清底层 SendInputEvent 到 Blink 事件的前向链路,就能在自动化测试、远程操控、无障碍辅助等场景中可靠地向页面注入按键。

KeyboardInputEvent 对象定义

根据 keyboard-input-event.md 的官方定义:

KeyboardInputEvent Object extends InputEvent

* type string - 事件类型,可以是 rawKeyDown、keyDown、keyUp 或 char
* keyCode string - 将作为键盘事件发送的按键。
  只能使用合法的 Accelerator 键码

两点关键约束:

  1. type 只有四种合法取值rawKeyDown(原始按键按下)、keyDown(按键按下)、keyUp(按键释放)、char(产生字符的按键)。
  2. keyCode 必须是字符串,且必须是合法的 Accelerator 键码。键码的完整清单与书写规则定义在 keyboard-shortcuts.md 的 Accelerators 章节。已确认支持的特殊键包括 PlusSpaceTabCapslockNumlockScrolllockBackspaceDeleteInsertReturn(或别名 Enter),字母、数字及方向键等亦在该清单中。

继承自 InputEvent 的通用字段

KeyboardInputEvent 继承自 InputEvent 对象,因此还可携带基类字段:

字段 类型 说明
type string 事件类型(键盘事件为上述四种之一)
modifiers string[](可选) 修饰键数组,可取 shiftcontrolctrlaltmetacommandcmdiskeypadisautorepeatleftbuttondownmiddlebuttondownrightbuttondowncapslocknumlockleftright

gin 转换器源码 看,modifiers 数组在 C++ 侧会被逐位映射为 blink::WebInputEvent::Modifiers 位掩码,时间戳则由原生侧用 base::TimeTicks::Now() 填充,JS 侧无需也不应提供。

使用方式:webContents.sendInputEvent()

KeyboardInputEvent 的唯一官方消费入口是 webContents.sendInputEvent(inputEvent)

// inputEvent 的合法类型为:
// MouseInputEvent | MouseWheelInputEvent | KeyboardInputEvent
contents.sendInputEvent(inputEvent)

文档中有一个重要注意事项:包含该 webContentsBrowserWindow 必须处于聚焦状态,sendInputEvent() 才能生效。这是编写自动化脚本时最容易踩的坑。

典型的注入写法(与仓库测试用例一致):

const { app, BrowserWindow, ipcMain } = require('electron');

app.whenReady().then(async () => {
  const win = new BrowserWindow({ show: true });
  await win.loadFile('pages/key-events.html'); // 页面中监听 keydown/keypress/keyup

  // 模拟按下 Shift+Ctrl+Z
  win.webContents.sendInputEvent({
    type: 'keyDown',
    keyCode: 'Z',
    modifiers: ['shift', 'ctrl']
  });

  // 模拟字符输入:'char' 事件会让 <input> 真正收到文本
  win.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A' });
  win.webContents.sendInputEvent({ type: 'char', keyCode: 'A' });

  // 模拟按键释放
  win.webContents.sendInputEvent({ type: 'keyUp', keyCode: 'Z' });
});

仓库的端到端测试位于 api-web-contents-spec.ts,被测页面为 key-events.html。几个可直接复用的验证结论:

  • sendInputEvent({ type: 'keyDown', keyCode: 'A' }) 在页面侧收到 key === 'a'code === 'KeyA'keyCode === 65,修饰键位均为 false;
  • 加上 modifiers: ['shift', 'ctrl'] 后,keyCode: 'Z' 产生的 key 为大写 'Z',且 shiftKeyctrlKey 均为 true;
  • 特殊键 keyCode: 'Tab' 产生 keyCode === 9code === 'Tab'
  • <input> 输入可见字符时,需要 keyDownchar 事件配合。例如连续发送 char 类型的 PlusSpace,输入框最终内容为 '+ + +'——也就是说 Plus/Space 这类加速器键码会被转换成真实字符 + 和空格,而不是字面字符串。

源码级实现:从 JS 对象到 Blink 事件

分发入口 SendInputEvent

electron_api_web_contents.cc 的 WebContents::SendInputEventsendInputEvent 的 C++ 实现,流程如下:

  1. 通过 web_contents()->GetRenderWidgetHostView() 获取渲染视图,若不存在直接返回(这解释了为何页面未就绪时事件会被静默丢弃);
  2. gin::GetWebInputEventType 读取 type 字段并判断事件族;
  3. 键盘分支(IsKeyboardEventType)中构造 input::NativeWebKeyboardEvent,通过 gin 从 V8 对象转换,随后调用 rwh->ForwardKeyboardEvent(keyboard_event) 交给 RenderWidgetHost 前向到 Blink。

值得注意的两处行为:

  • 向后兼容:源码中显式将 kKeyDown 改写为 kRawKeyDown(见 该分支 L3963-L3966),因此 keyDownrawKeyDown 在原生路径上最终等价;
  • 失败抛错:类型无法识别或字段缺失时,会以 Invalid event object 抛出 JS 异常,而不是静默忽略。

gin 转换器:keyCode 如何被解析

键盘事件的字段解析分两层 gin 转换器:

  • content_converter.cc 中的 NativeWebKeyboardEvent 转换器:先按 WebKeyboardEvent 解析基础字段,再额外读取一个文档未列出的布尔字段 skipIfUnhandled——从源码结构看,该字段可控制“事件未被页面处理时是否继续向下分发”,属于进阶可用字段;
  • blink_converter.cc 中的 WebKeyboardEvent 转换器:核心逻辑是把 keyCode 字符串交给 electron::KeyboardCodeFromStr 解析为 ui::KeyboardCode,再经 ui::UsLayoutKeyboardCodeToDomCode 推导 dom_codedom_key。若解析失败(即 keyCode 不是合法 Accelerator 键码),整个转换失败并触发上述异常。对 char / rawKeyDown 类型,源码还会做字符替换:当键码解析出的 domKey 是可见字符(长度大于 1 的如 SpacePlus)时,用实际字符(' ''+')填充事件的 textunmodified_text——这正是前文“输入 Space 得到空格”现象的根源。此外,KeyboardCodeFromStr 若要求 Shift 才能产生该键(例如 Plus 对应 Shift+=),会自动叠加 kShiftKey 修饰位。

事件流总览

JS: webContents.sendInputEvent({type, keyCode, modifiers})
  → C++: WebContents::SendInputEvent (electron_api_web_contents.cc)
      gin 转换 keyCode → ui::KeyboardCode / dom_code / dom_key
      keyDown 兼容改写为 rawKeyDown
  → rwh->ForwardKeyboardEvent(NativeWebKeyboardEvent)
  → Blink 渲染管线 → 页面收到 keydown / keypress / input / keyup 事件

实践要点与常见陷阱

  1. 窗口必须先聚焦:官方文档明确提示,BrowserWindow 未聚焦时 sendInputEvent 不生效。自动化场景建议在发送前显式 win.show() / win.focus() 并等待 ready-to-show
  2. 想往输入框写入文本,请成对发送 keyDown + char:仅发 keyDown 只会产生按键事件,不会产生 input/文本变更;char 事件的 keyCode 决定写入的字符。
  3. 特殊键请写 Accelerator 键码而非 DOM codekeyCode 只认 Accelerator 键码(TabReturnPlus、方向键等),传入非法字符串会抛 Invalid event object
  4. keyDownrawKeyDown 行为一致:原生层做了兼容合并,二选一即可;需要 char 语义时才用 char 类型。
  5. 修饰键通过 modifiers 数组表达:支持 shiftcontrol/ctrlaltmeta/command/cmd 等别名,数组元素会按位合并进原生修饰掩码。
  6. 进阶字段:从转换器源码可推断还支持 skipIfUnhandled 布尔字段(文档未列出),是否使用建议结合具体渲染管线行为验证后再引入。

相关文档与源码索引

内容 路径
KeyboardInputEvent 结构定义 docs/api/structures/keyboard-input-event.md
基类 InputEvent 结构 docs/api/structures/input-event.md
sendInputEvent 方法说明 docs/api/web-contents.md
Accelerator 键码清单 docs/tutorial/keyboard-shortcuts.md
C++ 分发实现 shell/browser/api/electron_api_web_contents.cc
键码/字符解析转换器 shell/common/gin_converters/blink_converter.cc
NativeWebKeyboardEvent 转换器 shell/common/gin_converters/content_converter.cc
键盘事件端到端测试 spec/api-web-contents-spec.ts
测试用按键监听页面 spec/fixtures/pages/key-events.html
登录后查看全文
热门项目推荐
相关项目推荐