伟大的程序员都懒。
这话是我从《PHP 与 MySQL 程序设计》中看来的,来自于 Larry Wall 的一句话:
Most of you are familiar with the virtues of a programmer. There are three, of course: laziness, impatience, and hubris.
懒的程序员的特征是:能花一步完成的事绝不花两步,即便花一步那一步的时间也是越少越好。所以他们做了很多工具来快捷完成一些繁琐耗时长的任务;放到 Web 上,就有人做了快捷键;这个技术难度并不高,但是把一些非常频繁的操作利用快捷键来触发的话,速度会快不少;毕竟,用鼠标在屏幕上定位一个点然后点击,是比定位键盘按键速度慢的。
下图分别是 Github、Facebook、Twitter、微博、知乎、Gitlab 的快捷键,不知道你以前有没有注意过,如果没有,下次打开这些网站的时候,在页面中输入「?」试试。
键码与键名的映射表
首先你需要定义一张键码与键名的映射表:因为我们在文档上监听键盘相关的事件,keyup
和keydown
,事件触发时根据我们获取的事件对象,能让我们判断是哪个按键的,只有事件对象的which
和keyCode
属性,而这都是以键码给出的,并不直观,尤其是在插件完成后注册快捷键时,非常不容易记忆和理解;
把你希望构成快捷键组合的所有按键,全部存进映射表中,可用如下方式给出:
var keyCode2keyName = {
9:'tab',
32:'space',
191:'?',
187:'+',
189:'-',
13:'enter'
}
for(var i = 65;i<91;i++){
keyCode2keyName[i] = String.fromCharCode(i).toLowerCase();
}
在后续的程序中必要的时候,我们都需要把获取到的键码转换成键名,方便理解。
思路
实际上,我们要解决的最主要的两个大问题:判断按键组合,触发组合事件;通俗地说就是:如何获取用户按下的快捷键(或者组合);用户按下组合按键后如何触发事件。
判断按键组合
我之前一直说的是按键组合,但其实不一定要全部定义多个按键按下才能触发快捷功能,我们完全可以定义某个单个按键被按下时就触发某个行为;总体来说,各个实现了快捷键功能的网站,快捷键种类有以下三种:
-
单个按键触发:比如
j
、k
;按j
选择下一个列表项,k
选择上一个列表项(可能灵感来自 VIM 编辑器); -
带修饰键的单个按键触发:修饰键指的是
shift
、control
、command
、alt
等等,通常在一个键盘事件触发时,自动生成的事件对象中,会有专门的属性指明某个修饰键是否被同时按下,其属性值是个布尔值; -
多个按键的触发:这里多个按键特指多个非修饰键的按键组合,比如
g+m
,意味着按下g
键之后继续按下m
键的组合;
当我们想做一个比较合格的插件时,需要能够处理以上三种情况;以及这三种情况的冲突解决。这里的冲突的指的是:假如我们既定义了a
执行某个功能Fa
,又定义了b+a
执行某个功能Fb
,那么当用户按下b
键之后继续按下a
键,那么程序应当如何响应?是执行Fa
还是Fb
或者是两者都执行。
我的建议是:在注册快捷键,尽量避免这样的冲突;如果实在无法避免,那这种情况下必须执行Fb
,因为如果连用户已经按了多个按键,程序还不触发组合按键事件,那组合快捷键就永远捕获不到了;优先捕获按键组合,其次捕获单个按键。
在 Bugtags 网站上有一个快捷键组合m+y
,可以快捷跳转到所有指派给「我」完成的问题;后面的叙述以这个例子来说明。
在快捷键的触发过程中,当某个按键被按下时,我们需要获取它与当前被按下的其他按键所能构成的组合。所以必然需要一个变量pressedKeys
(数组)来保存任意时刻被按下的按键,因此我们需要监听keyup
和keydown
事件;
当keydown
时,逻辑稍微复杂点,并且这也是整个快捷键功能的核心;
在某个按键按下时,需要考察当前按键和已经按下的其他按键,看看会构成哪些按键组合(拼接按键组合字符串,作为激活事件的依据):
在keydown
事件中只需要专门捕获按键组合,而不用考虑这个按键或者按键组合是否已经定义了执行某个方法。然后把捕获的组合传入另一个方法handleKeyCombination
,由他来查找这个按键组合是否定义,以及执行已定义的回调。举个例子:
-
假如先按下
a
,没有其他按键在这之前被按下,按键组合就是a
,同时a
存入pressedKeys
,执行handleKeyCombination
传入a
; -
继续按下
m
,构成按键组合a+m
,m
存入pressedKeys
,执行handleKeyCombination
传入a+m
; -
然后当
y
被按下时,当前按键跟已经按下的其他按键构成的组合包括a+y
和m+y
,如果按照严格一点的检测方式,只跟当前按键最近的一次按键匹配,就是m+y
,如果你需要定义三个按键的组合快捷键,那当前的按键组合是a+m+y
。(不过通常来说,两个按键的组合就已经够用了);
然后仍然要把当前键码存入pressedKeys
中;但是有一个特例:那就是修饰键。修饰键最好定义成与非修饰键的组合构成快捷键,在按住一个非修饰键时,我们可以通过查询事件对象来判断某个修饰键是否按下,而不需要通过前述的pressedKeys
;因此按下修饰键并不需要保存到pressedKeys
里;在 Bugtags 网站中,采用了严格的检测方式,执行handleKeyCombination
传入m+y
当keyup
时,事情就简单多了,把相应的键码从pressedKeys
中删除即可。
var pressedKeys = [];
// 核心逻辑都要在 keydown 事件的回调中处理
// 注:文中的代码全部依赖 jQuery;
$(document).on('keydown',function(e){
var key = '';
// 这里采用严格的检测方式,在所有与当前按键同时按下的键中,只选择最近按下的与当前按下的进行组合,即pressedKyes数组的最后一项
if(pressedKyes.length){
key = keyCode2keyName[pressedKeys[pressedKeys.length-1]]+'+'+keyCode2keyName[keyCode];
}else{
// 如果 pressedKeys 是空数组,说明没有其他按键被按下;此时检查修饰键是否按下;
if(e.shiftKey) key = 'shift';
if(e.ctrlKey) key = key?key+'+ctrl':'ctrl';
if(e.altKey) key = key?key+'+alt':'alt';
if(e.metaKey) key = key?key+'+meta':'meta';
key = key?key+'+'+keyCode2keyName[keyCode]:keyCode2keyName[keyCode];
}
// 将当前按键存入 pressedKyes;
pressedKeys.indexOf(keyCode)<0 && (keyCode in keyCode2keyName) && pressedKeys.push(keyCode);
handleKeyCombination(key);
});
$(document).on('keyup',function(e){
if(pressedKeys.indexOf(e.keyCode)>-1){
pressedKeys.splice(pressedKeys.indexOf(e.keyCode),1);
}
})
function handleKeyCombination(key){ // 暂时留空,后文会完善}
触发组合事件
接下来就是在handleKeyCombination
方法中,处理接收到的用户当前的按键组合,查询这个组合是否定义了回调,有就激活,没有则忽略。问题是如果这个按键组合已经定义了事件,那如何激活它呢?
要确定激活方式,就得确定事件的注册方式;我们需要实现一个事件注册方法,接受一个快捷键组合,以及相应回调;
有一种很直观的思路是这样的:注册这个组合对应的字符串为一个自定义事件,比如m+y
,传入的回调就是这个事件的回调,即:
// 注册事件
function registerHotKey(key,callback){
$(document).on(key,callback);
}
// 触发事件
$(document).trigger(key);
然后在用户按下m
以及y
之后,传入这个组合,直接trigger
这个事件,自然就会执行相应方法。如果是一个从未定义过回调的方法,同样trigger
,只不过它没有绑定事件所以什么都不做。
但是这样会有严重的性能问题:
-
注册一个快捷键就得注册一个自定义事件,
j
是一个,k
是一个,m+y
是一个……这样你注册的事件会越来越多,对性能是一个比较严重的损耗; -
假如你想停止使用快捷键功能,要么逐个解绑所有的快捷键事件。要么从源头上解绑
keyup
,keydown
相关事件。都是非常麻烦的。
统一的自定义事件
解决方法仍然是用自定义事件,不过全局我们只注册一个自定义事件,我们维持一个键值对(对象),键就是我们注册的快捷键组合,值就是当这个快捷键被触发时执行的方法。注册新的快捷键组合时,往这个对象中添加新的键值对即可。
当用户按键时,同样是将用户的按键组合传入方法,handleKeyCombination
,然后我们检测用户按下的按键组合作为属性是否存在于前述对象中,如果存在则触发一个统一的事件,并传入这个组合键,让这个统一的事件去分发不同快捷键对应的方法进行执行。
var definedKeys;
// 绑定一个统一的自定义事件
$(document).on('hotkey:active',function(keyCombination){
definedKeys[keyCombination].call();
})
// 注册事件
function register(key,callback){
if(!definedKeys){
definedKeys = {};
}
if(!(key in definedKeys)){
definedKeys[key] = callback;
}
}
// 触发事件
function handleKeyCombination(keyCombination){
if(keyCombination in definedKeys){
$(document).trigger('hotkey:active',keyCombination)
}
}
这样做很明显的好处是
-
注册新的事件时只需要操作
definedKeys
对象即可,不再需要再操作事件相关的逻辑,不再添加新的自定义事件。 -
可以随时解绑快捷键功能,只需要停止触发自定义的
hotkey:active
事件就可以了;
事件触发区域
另外还有一个需要格外注意的点是:我们监听键盘事件是绑定在整个文档对象 document 上的,但是由于浏览器的事件传播机制,如果用户在与表单交互,比如input
,textarea
,用户的文字输入行为最终会冒泡到 document 上,插件如果不分情况的进行监听则很不合理的;我们需要明确区分用户确实是要在页面中输入内容和用户想触发快捷键这两种行为,因此在keydown
事件中需要检测事件的target
,如果是一个表单交互对象就不要触发任何事件。
另外一些需要注意的细节
-
快捷键的选取
这个是仁者见仁智者见智,不过总的原则是:照顾用户习惯,好记;下面这些键跟其功能的匹配比较常用:k
,j
:选择列表项上一项、下一项,这个来自于 vim 编辑器;?
:显示快捷键窗口,向用户展示快捷键的组合 -
页面上下文:同一套快捷键在不同的页面执行不同的功能,大部分快捷键都是有上下文,即针对某一个页面的;如果到了另一个不同的页面页面仍然触发了快捷键,执行回调需要进行容错处理;
总结
本文简述了实现一个快捷键插件的思路,并提供了核心的代码予以说明;但是这些代码片段不足以实现完整地快捷键功能,详细的代码可参考 Bugtags 的 gist(https://gist.github.com/sunlianghua/b2467f3c7e739bb169a6),Bugtags 网站中的快捷键插件就是基于这个脚本。
相关推荐
熟练掌握这些快捷键可以让您轻松应对工作中出现的问题。本篇文章将为您详细介绍电脑常用的快捷键,帮助您提高工作效率和办公效率。 一、Windows 快捷键 1. WINKEY+L:锁屏 2. WINKEY+D:桌面 3. WINKEY+E:我的...
当你编写代码时,编译器会提示错误信息,这时,你可以使用 Ctrl+1 或 F2 快捷键来快速修复错误。这样可以节省时间和提高开发效率。 2. 快捷删除行:Ctrl+D 当你需要删除某一行代码时,可以使用 Ctrl+D 快捷键。这...
studio 快捷键
如果你想为某个命令设置新的快捷键,首先选择该命令,然后按下你想要的快捷键组合。系统会检查这个快捷键是否已被其他命令占用,如果没有冲突,这个快捷键就会被分配给选定的命令。如果有冲突,系统会提示你是否...
立创EDA快捷键,包含所有快捷键
Cadence Allegro是一款广泛...在学习和使用过程中,不断实践和调整,你会发现Allegro的快捷键设置能显著提升你的工作效率,使你在PCB设计中游刃有余。记得定期更新和优化你的快捷键配置,以适应不断变化的设计需求。
自定义快捷键可以让玩家根据自己的习惯和游戏需求设定,比如将常用的技能、道具或行动绑定到易按的键盘组合上,从而提高游戏体验和竞技表现。 关于快捷键的知识点包括: 1. **快捷键的作用**:快捷键能够帮助用户...
通过不断的实践和调整,你会找到最适合自己的快捷键组合,让Eclipse成为你得心应手的开发工具。同时,可以参考《如何配置快捷键.wmv》这个视频教程,它将更直观地演示配置过程,帮助你更好地理解和操作。
金蝶 KIS 快捷键大全 金蝶 KIS 是一款功能强大且流行的财务软件,它提供了许多实用的快捷键,帮助用户提高工作效率。下面是金蝶 KIS 中常用的快捷键: Navigation 快捷键 * Enter: 在凭证或业务界面一般输入框...
### Photoshop基本工具快捷键详解 #### 一、工具箱快捷键 Photoshop是一款强大的图像处理软件,通过快捷键可以极大提高工作效率。以下是常用的工具箱快捷键: - **矩形、椭圆选框工具**:使用快捷键【M】。若需要...
广联达 快捷键 大全,让广联达用起来更方便
NXP S32DS 快捷键
SAP ABAP开发快捷键 SAP ABAP开发是一种功能强大且灵活的编程语言,用于开发SAP系统中的业务逻辑。然而,在ABAP开发过程中,效率是一个非常重要的因素。为了提高开发效率,熟悉ABAP开发快捷键是非常必要的。在本文...
在Windows Presentation Foundation (WPF) 中,为窗体自定义快捷键可以极大地提高用户体验,使得用户能够更快捷地操作应用程序。本教程将深入探讨如何在WPF应用中实现这一功能,同时,这些方法同样适用于Windows ...
### MyEclipse 代码提示快捷键设置 在开发过程中,高效地进行代码编写是提高工作效率的关键之一。MyEclipse作为一款流行的Java集成开发环境(IDE),提供了丰富的功能来辅助开发者进行编码工作。其中,代码提示功能...
3. Ctrl + E:快速转换编辑器:这组快捷键可以帮助你在打开的编辑器之间快速浏览,使用 Ctrl + Page Down 或 Ctrl + Page Up 可以浏览前后的选项卡,但是在很多文件打开的状态下,Ctrl + E 会更加有效率。...
as 快捷键
"Idea IDE 快捷键大全" Idea IDE 快捷键大全是指在 Intellij Idea 集成开发环境中使用的快捷键的集合,这些快捷键可以帮助开发者提高编程效率、提高开发速度和质量。在这个大全中,我们将涵盖编辑、代码补全、代码...
在这样的环境下,你可以使用以下快捷键: - **空格**:向下翻页。 - **b**:向上翻页。 - **/`text`**:查找包含`text`的行。 - **n**:查找下一个匹配项。 - **N**:查找上一个匹配项。 - **q**:退出浏览。 ### ...
很好,很实用,cad快捷键 各个行业都好用的快捷键,下吧。我要你们资源分