js 键盘监听 组合键

2024-08-24 04:04
文章标签 js 键盘 监听 组合键

本文主要是介绍js 键盘监听 组合键,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

今天分享如何快速实现js快捷键监听

所需环境:

  • 浏览器
  • js

实现目标

  • mac/win兼容,一套代码,多个平台
  • 支持快捷键监听/单按键监听
  • 事件是否冒泡可设置
  • 使用方式简单
  • 快速挂载与卸载
  • 4行代码实现组合键监听

代码原理

把键盘监听事件挂载在document上,当键盘事件发生时,依次匹配事先订阅的快捷键/单按键事件,
如果有匹配到对应的订阅,则进行事件回调,并且停止键盘事件的回调。按下的按键进行转换,把mac/win的键盘差异进行了兼容

实现效果

在这里插入图片描述

js快捷键实战

核心代码

interface keyListener {keys: Array<string | ((event: KeyboardEvent) => boolean)>;// ALL 全部符合才算| ANY 匹配到任意一个就算matchType: 'ALL' | 'ANY';callback: (keyStr: string) => void;// 是否停止时间传播stop: boolean;
}// 多平台键盘转换
const keyConvert = {Ctrl: ['Meta', 'Ctrl']
} as Record<string, Array<string>>;const eventListeners: Array<keyListener> = [];
const downKeyList = {} as any;export function addKeyboardEvent() {document.addEventListener('keydown', keyDown);document.addEventListener('keyup', keyUp);const handlers = {subscribe(keys: Array<string | ((event: KeyboardEvent) => boolean)>, callback: (keyStr: string) => void, stop = true) {eventListeners.push({keys,matchType: 'ALL',callback,stop});return handlers;},subscribeAny(keys: Array<string | ((event: KeyboardEvent) => boolean)>, callback: (keyStr: string) => void, stop = true) {eventListeners.push({keys,matchType: 'ANY',callback,stop});return handlers;}};return handlers;
}export function removeKeyboardEvent() {document.removeEventListener('keydown', keyDown);document.removeEventListener('keyup', keyUp);
}function keyUp(event: KeyboardEvent) {delete downKeyList[convertKey(event.key)];
}function convertKey(key: string) {for (let keyConvertKey in keyConvert) {let convertList = keyConvert[keyConvertKey];if (convertList.includes(key)) {return keyConvertKey;}}return key;
}function keyDown(event: KeyboardEvent) {// 如果需要输入框,则不监听组合键if ((event.target as HTMLElement).tagName === 'INPUT' || (event.target as HTMLElement).tagName === 'TEXTAREA') {// console.log('This event is triggered by an input or textarea!');return;}for (let eventListener of eventListeners) {let matchResult = falseif (eventListener.matchType == 'ALL') {matchResult = matchAll(event, eventListener)} else {matchResult = matchAny(event, eventListener)}if (matchResult) {break}}
}function matchAll(event: KeyboardEvent, keyListener: keyListener) {const {keys, callback, stop} = keyListener;let isTrigger = true;let keyStr = ''for (let key of keys) {if (key instanceof Function) {keyStr = keyStr + key.name + "+"if (!key(event)) {isTrigger = false;break;}} else {keyStr = keyStr + event.key + "+"if (key != event.key) {isTrigger = false;break;}}}if (isTrigger) {keyStr = keyStr.slice(0, -1);callback(keyStr);if (stop) {event.preventDefault();event.stopPropagation();}return true;}return false
}function matchAny(event: KeyboardEvent, keyListener: keyListener) {const {keys, callback, stop} = keyListener;let isTrigger = false;let keyStr = ''for (let key of keys) {if (key instanceof Function) {if (key(event)) {keyStr = keyStr + key.name + "+"isTrigger = true;}} else {if (key == event.key) {keyStr = keyStr + event.key + "+"isTrigger = true;}}if (isTrigger) {keyStr = keyStr.slice(0, -1);callback(keyStr);if (stop) {event.preventDefault();event.stopPropagation();}return true}}return false
}// 兼容macos & win
export function isCtrl(event: KeyboardEvent) {return event.ctrlKey || event.metaKey;
}export function isShift(event: KeyboardEvent) {return event.shiftKey;
}export function isCtrlShift(event: KeyboardEvent) {return (event.ctrlKey || event.metaKey) && event.shiftKey;
}export function isDelete(event: KeyboardEvent) {return event.key == 'Backspace';
}

使用示例

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>MyPrint-快捷键示例</title><style>:root {font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;line-height: 1.5;font-weight: 400;color-scheme: light dark;color: rgba(255, 255, 255, 0.87);background-color: #242424;font-synthesis: none;text-rendering: optimizeLegibility;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}body {display: flex;height: 100vh;place-items: center;width: 100%;min-height: 100vh;margin: 0 auto;text-align: center;}#keyboardShortcutsStr {width: 100%;}</style>
</head>
<body>
<div id="keyboardShortcutsStr"></div>
<script>function setKeyboardShortcutsStr(keyboardShortcutsStr) {document.querySelector("#keyboardShortcutsStr").innerHTML = keyboardShortcutsStr}var keyConvert = {Ctrl: ['Meta', 'Ctrl']};var eventListeners = [];var downKeyList = {};function addKeyboardEvent() {document.addEventListener('keydown', keyDown);document.addEventListener('keyup', keyUp);var handlers = {subscribe: function (keys, callback, stop) {if (stop === void 0) {stop = true;}eventListeners.push({keys: keys,matchType: 'ALL',callback: callback,stop: stop});return handlers;},subscribeAny: function (keys, callback, stop) {if (stop === void 0) {stop = true;}eventListeners.push({keys: keys,matchType: 'ANY',callback: callback,stop: stop});return handlers;}};return handlers;}function removeKeyboardEvent() {document.removeEventListener('keydown', keyDown);document.removeEventListener('keyup', keyUp);}function keyUp(event) {delete downKeyList[convertKey(event.key)];}function convertKey(key) {for (var keyConvertKey in keyConvert) {var convertList = keyConvert[keyConvertKey];if (convertList.includes(key)) {return keyConvertKey;}}return key;}function keyDown(event) {// 如果需要输入框,则不监听组合键if (event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA') {// console.log('This event is triggered by an input or textarea!');return;}for (var _i = 0, eventListeners_1 = eventListeners; _i < eventListeners_1.length; _i++) {var eventListener = eventListeners_1[_i];var matchResult = false;if (eventListener.matchType == 'ALL') {matchResult = matchAll(event, eventListener);} else {matchResult = matchAny(event, eventListener);}if (matchResult) {break;}}}function matchAll(event, keyListener) {var keys = keyListener.keys, callback = keyListener.callback, stop = keyListener.stop;var isTrigger = true;var keyStr = '';for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {var key = keys_1[_i];if (key instanceof Function) {keyStr = keyStr + key.name + "+";if (!key(event)) {isTrigger = false;break;}} else {keyStr = keyStr + event.key + "+";if (key != event.key) {isTrigger = false;break;}}}if (isTrigger) {keyStr = keyStr.slice(0, -1);callback(keyStr);if (stop) {event.preventDefault();event.stopPropagation();}return true;}return false;}function matchAny(event, keyListener) {var keys = keyListener.keys, callback = keyListener.callback, stop = keyListener.stop;var isTrigger = false;var keyStr = '';for (var _i = 0, keys_2 = keys; _i < keys_2.length; _i++) {var key = keys_2[_i];if (key instanceof Function) {if (key(event)) {keyStr = keyStr + key.name + "+";isTrigger = true;}} else {if (key == event.key) {keyStr = keyStr + event.key + "+";isTrigger = true;}}if (isTrigger) {keyStr = keyStr.slice(0, -1);callback(keyStr);if (stop) {event.preventDefault();event.stopPropagation();}return true;}}return false;}// 兼容macos & winfunction isCtrl(event) {return event.ctrlKey || event.metaKey;}function isShift(event) {return event.shiftKey;}function isCtrlShift(event) {return (event.ctrlKey || event.metaKey) && event.shiftKey;}function isDelete(event) {return event.key == 'Backspace';}addKeyboardEvent()// macos.subscribe([isCtrlShift, 'z'], (_keyStr) => {setKeyboardShortcutsStr('Ctrl+Shift+z 重做')})// win.subscribe([isCtrlShift, 'y'], () => {setKeyboardShortcutsStr('Ctrl+y')}).subscribe([isCtrl, 'z'], () => {setKeyboardShortcutsStr('Ctrl+z 撤销')}).subscribe([isCtrl, 'a'], () => {setKeyboardShortcutsStr('Ctrl+a 全选')}).subscribe([isCtrl, 'c'], () => {setKeyboardShortcutsStr('Ctrl+c 复制')}).subscribe([isCtrl, 'x'], () => {setKeyboardShortcutsStr('Ctrl+c 剪切')}).subscribe([isCtrl, 'v'], () => {setKeyboardShortcutsStr('Ctrl+v 粘贴')}).subscribe([isCtrl, 'd'], () => {setKeyboardShortcutsStr('Ctrl+d 副本')}).subscribe([isCtrl, 's'], () => {setKeyboardShortcutsStr('Ctrl+s 保存')}).subscribe([isCtrl, 'f'], () => {setKeyboardShortcutsStr('Ctrl+f 搜索')}).subscribe(['Tab'], () => {setKeyboardShortcutsStr('Tab切换')}).subscribe([isCtrlShift, 'ArrowUp'], () => {// console.log('ArrowUp')}).subscribe([isCtrlShift, 'ArrowDown'], () => {// console.log('ArrowDown')}).subscribe([isCtrlShift, 'ArrowLeft'], () => {// console.log('ArrowLeft')}).subscribe([isCtrlShift, 'ArrowRight'], () => {// console.log('ArrowRight')}).subscribe([isShift, 'ArrowUp'], () => {// console.log('ArrowUp')}).subscribe([isShift, 'ArrowDown'], () => {}).subscribe([isShift, 'ArrowLeft'], () => {}).subscribe([isShift, 'ArrowRight'], () => {}).subscribe([isCtrl, 'ArrowUp'], () => {}).subscribe([isCtrl, 'ArrowDown'], () => {}).subscribe([isCtrl, 'ArrowLeft'], () => {}).subscribe([isCtrl, 'ArrowRight'], () => {}).subscribe(['ArrowUp'], () => {}).subscribe(['ArrowDown'], () => {}).subscribe(['ArrowLeft'], () => {}).subscribe(['ArrowRight'], () => {}).subscribe([isDelete], () => {// console.log('ArrowRight')}).subscribeAny(['q', 'w', 'e', 'r', 't', 'a', 'b', 'c', 'd'], (keyStr) => {// console.log('ArrowRight')setKeyboardShortcutsStr(keyStr)}, false);
</script>
</body>
</html>

代码仓库

在线体验

代码仓库:github

代码仓库:gitee

实战项目:MyPrint

操作简单,组件丰富的一站式打印解决方案打印设计器

体验地址:前往

代码仓库:github

代码仓库:gitee

这篇关于js 键盘监听 组合键的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1101389

相关文章

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的

Node.js 数据库 CRUD 项目示例详解(完美解决方案)

《Node.js数据库CRUD项目示例详解(完美解决方案)》:本文主要介绍Node.js数据库CRUD项目示例详解(完美解决方案),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考... 目录项目结构1. 初始化项目2. 配置数据库连接 (config/db.js)3. 创建模型 (models/

使用Node.js制作图片上传服务的详细教程

《使用Node.js制作图片上传服务的详细教程》在现代Web应用开发中,图片上传是一项常见且重要的功能,借助Node.js强大的生态系统,我们可以轻松搭建高效的图片上传服务,本文将深入探讨如何使用No... 目录准备工作搭建 Express 服务器配置 multer 进行图片上传处理图片上传请求完整代码示例

使用Python实现全能手机虚拟键盘的示例代码

《使用Python实现全能手机虚拟键盘的示例代码》在数字化办公时代,你是否遇到过这样的场景:会议室投影电脑突然键盘失灵、躺在沙发上想远程控制书房电脑、或者需要给长辈远程协助操作?今天我要分享的Pyth... 目录一、项目概述:不止于键盘的远程控制方案1.1 创新价值1.2 技术栈全景二、需求实现步骤一、需求

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Flutter监听当前页面可见与隐藏状态的代码详解

《Flutter监听当前页面可见与隐藏状态的代码详解》文章介绍了如何在Flutter中使用路由观察者来监听应用进入前台或后台状态以及页面的显示和隐藏,并通过代码示例讲解的非常详细,需要的朋友可以参考下... flutter 可以监听 app 进入前台还是后台状态,也可以监听当http://www.cppcn

spring @EventListener 事件与监听的示例详解

《spring@EventListener事件与监听的示例详解》本文介绍了自定义Spring事件和监听器的方法,包括如何发布事件、监听事件以及如何处理异步事件,通过示例代码和日志,展示了事件的顺序... 目录1、自定义Application Event2、自定义监听3、测试4、源代码5、其他5.1 顺序执行

Node.js net模块的使用示例

《Node.jsnet模块的使用示例》本文主要介绍了Node.jsnet模块的使用示例,net模块支持TCP通信,处理TCP连接和数据传输,具有一定的参考价值,感兴趣的可以了解一下... 目录简介引入 net 模块核心概念TCP (传输控制协议)Socket服务器TCP 服务器创建基本服务器服务器配置选项服

mac安装nvm(node.js)多版本管理实践步骤

《mac安装nvm(node.js)多版本管理实践步骤》:本文主要介绍mac安装nvm(node.js)多版本管理的相关资料,NVM是一个用于管理多个Node.js版本的命令行工具,它允许开发者在... 目录NVM功能简介MAC安装实践一、下载nvm二、安装nvm三、安装node.js总结NVM功能简介N