Обнаружение нескольких нажатий клавиш легко, если вы понимаете концепцию
То, как я это делаю, выглядит так:
var map = {}; // You could also use an array
onkeydown = onkeyup = function(e){
e = e || event; // to deal with IE
map[e.keyCode] = e.type == 'keydown';
/* insert conditional here */
}
Этот код очень прост: поскольку компьютер одновременно выполняет только одно нажатие клавиши, создается массив для отслеживания нескольких клавиш. Затем этот массив можно использовать для проверки одного или нескольких ключей одновременно.
Просто чтобы объяснить, скажем, вы нажимаете A и B , каждый из которых запускает событие keydown
, которое устанавливает map[e.keyCode]
в значение e.type == keydown
, которое оценивается как либо true , либо false . Теперь map[65]
и map[66]
установлены на true
. Когда вы отпускаете A
, происходит событие keyup
, в результате чего та же логика определяет противоположный результат для map[65]
(A), который теперь равен false , но с map[66]
( B) все еще "вниз" (это не вызвало событие keyup), оно остается true .
Массив map
через оба события выглядит следующим образом:
// keydown A
// keydown B
[
65:true,
66:true
]
// keyup A
// keydown B
[
65:false,
66:true
]
Есть две вещи, которые вы можете сделать сейчас:
A) Регистратор ключей ( пример ) может быть создан в качестве справочного материала для дальнейшего использования, если вы хотите быстро выяснить один или несколько кодов клавиш. Предполагая, что вы определили элемент html и указали на него с помощью переменной element
.
element.innerHTML = '';
var i, l = map.length;
for(i = 0; i < l; i ++){
if(map[i]){
element.innerHTML += '<hr>' + i;
}
}
Примечание. Вы можете легко получить элемент по его атрибуту id
.
<div id="element"></div>
Это создает элемент HTML, на который можно легко ссылаться в javascript с помощью element
alert(element); // [Object HTMLDivElement]
Вам даже не нужно использовать document.getElementById()
или $()
, чтобы захватить его. Но для совместимости более широко рекомендуется использовать jQuery $()
.
Просто убедитесь, что тег script идет после тела HTML. Совет по оптимизации : Большинство известных веб-сайтов ставят тег скрипта после тега body для оптимизации. Это связано с тем, что тег script блокирует дальнейшую загрузку элементов до завершения загрузки скрипта. Выдвижение его перед контентом позволяет загружать контент заранее.
B (в этом и состоит ваш интерес) Вы можете проверить наличие одного или нескольких ключей в то время, когда было /*insert conditional here*/
, возьмите этот пример:
if(map[17] && map[16] && map[65]){ // CTRL+SHIFT+A
alert('Control Shift A');
}else if(map[17] && map[16] && map[66]){ // CTRL+SHIFT+B
alert('Control Shift B');
}else if(map[17] && map[16] && map[67]){ // CTRL+SHIFT+C
alert('Control Shift C');
}
Редактировать : Это не самый читаемый фрагмент. Читаемость важна, поэтому вы можете попробовать что-то вроде этого, чтобы было легче смотреть:
function test_key(selkey){
var alias = {
"ctrl": 17,
"shift": 16,
"A": 65,
/* ... */
};
return key[selkey] || key[alias[selkey]];
}
function test_keys(){
var keylist = arguments;
for(var i = 0; i < keylist.length; i++)
if(!test_key(keylist[i]))
return false;
return true;
}
Использование:
test_keys(13, 16, 65)
test_keys('ctrl', 'shift', 'A')
test_key(65)
test_key('A')
Это лучше?
if(test_keys('ctrl', 'shift')){
if(test_key('A')){
alert('Control Shift A');
} else if(test_key('B')){
alert('Control Shift B');
} else if(test_key('C')){
alert('Control Shift C');
}
}
(конец редактирования)
В этом примере проверяется Ctrl Shift A , Ctrl Shift B и Ctrl Shift C
Это так просто:)
Примечания
Отслеживание кодов клавиш
Как правило, рекомендуется документировать код, особенно такие, как коды клавиш (например, // CTRL+ENTER
), чтобы вы могли запомнить, что они были.
Вы также должны располагать коды клавиш в том же порядке, что и документация (CTRL+ENTER => map[17] && map[13]
, НЕ map[13] && map[17]
). Таким образом, вы никогда не запутаетесь, когда вам нужно вернуться и отредактировать код.
Гоча с цепями if-else
При проверке комбинаций различных сумм (например, Ctrl Shift Alt Введите и Ctrl Введите ), поместите меньшие комбо после больших комбо, иначе меньшие комбо заменит более крупные комбо, если они достаточно похожи. Пример:
// Correct:
if(map[17] && map[16] && map[13]){ // CTRL+SHIFT+ENTER
alert('Whoa, mr. power user');
}else if(map[17] && map[13]){ // CTRL+ENTER
alert('You found me');
}else if(map[13]){ // ENTER
alert('You pressed Enter. You win the prize!')
}
// Incorrect:
if(map[17] && map[13]){ // CTRL+ENTER
alert('You found me');
}else if(map[17] && map[16] && map[13]){ // CTRL+SHIFT+ENTER
alert('Whoa, mr. power user');
}else if(map[13]){ // ENTER
alert('You pressed Enter. You win the prize!');
}
// What will go wrong: When trying to do CTRL+SHIFT+ENTER, it will
// detect CTRL+ENTER first, and override CTRL+SHIFT+ENTER.
// Removing the else's is not a proper solution, either
// as it will cause it to alert BOTH "Mr. Power user" AND "You Found Me"
Получил: "Эта комбинация клавиш продолжает активироваться, хотя я не нажимаю клавиши"
При работе с оповещениями или чем-либо, что фокусируется на главном окне, вы можете включить map = []
для сброса массива после выполнения условия. Это потому, что некоторые вещи, такие как alert()
, отвлекают внимание от главного окна и приводят к тому, что событие 'keyup' не запускается. Например:
if(map[17] && map[13]){ // CTRL+ENTER
alert('Oh noes, a bug!');
}
// When you Press any key after executing this, it will alert again, even though you
// are clearly NOT pressing CTRL+ENTER
// The fix would look like this:
if(map[17] && map[13]){ // CTRL+ENTER
alert('Take that, bug!');
map = {};
}
// The bug no longer happens since the array is cleared
Получено: настройки браузера по умолчанию
Вот досадная вещь, которую я нашел, с включенным решением:
Проблема: поскольку браузер обычно выполняет действия по умолчанию для сочетаний клавиш (например, Ctrl D активирует окно закладок или Ctrl Shift C активирует skynote в maxthon), вы также можете добавить return false
после map = []
, чтобы пользователи вашего сайта не расстраивались, когда включалась функция «Duplicate File» Ctrl D , вместо этого добавляется закладка на страницу.
if(map[17] && map[68]){ // CTRL+D
alert('The bookmark window didn\'t pop up!');
map = {};
return false;
}
Без return false
, окно Закладки всплыло бы , к ужасу пользователя.
Оператор возврата (новый)
Хорошо, так что вы не всегда хотите выйти из функции в этот момент. Вот почему есть функция event.preventDefault()
. Он устанавливает внутренний флаг, который указывает интерпретатору , а не , что позволяет браузеру запускать действие по умолчанию. После этого выполнение функции продолжается (тогда как return
немедленно выйдет из функции).
Поймите это различие, прежде чем решить, использовать ли return false
или e.preventDefault()
event.keyCode
устарело
Пользователь SeanVieira указал в комментариях, что event.keyCode
устарело.
Там он дал отличную альтернативу: event.key
, которая возвращает строковое представление нажатой клавиши, например "a"
для A или "Shift"
для Shift .
Я пошел вперед и приготовил инструмент для проверки указанных струн.
element.onevent
против element.addEventListener
Обработчики, зарегистрированные с помощью addEventListener
, могут быть сложены и вызываются в порядке регистрации, в то время как установка .onevent
напрямую довольно агрессивна и отменяет все, что у вас было ранее.
document.body.onkeydown = function(ev){
// do some stuff
ev.preventDefault(); // cancels default actions
return false; // cancels this function as well as default actions
}
document.body.addEventListener("keydown", function(ev){
// do some stuff
ev.preventDefault() // cancels default actions
return false; // cancels this function only
});
Кажется, что свойство .onevent
перекрывает все, и поведение ev.preventDefault()
и return false;
может быть довольно непредсказуемым.
В любом случае, обработчики, зарегистрированные через addEventlistener
, кажутся более простыми для написания и рассуждений.
Существует также attachEvent("onevent", callback)
от нестандартной реализации Internet Explorer, но это не рекомендуется и даже не относится к JavaScript (это относится к эзотерическому языку, называемому JScript ). В ваших интересах было бы как можно больше избегать кода полиглота.
класс помощника
Чтобы устранить путаницу / жалобы, я написал «класс», который делает эту абстракцию ( pastebin link ):
function Input(el){
var parent = el,
map = {},
intervals = {};
function ev_kdown(ev)
{
map[ev.key] = true;
ev.preventDefault();
return;
}
function ev_kup(ev)
{
map[ev.key] = false;
ev.preventDefault();
return;
}
function key_down(key)
{
return map[key];
}
function keys_down_array(array)
{
for(var i = 0; i < array.length; i++)
if(!key_down(array[i]))
return false;
return true;
}
function keys_down_arguments()
{
return keys_down_array(Array.from(arguments));
}
function clear()
{
map = {};
}
function watch_loop(keylist, callback)
{
return function(){
if(keys_down_array(keylist))
callback();
}
}
function watch(name, callback)
{
var keylist = Array.from(arguments).splice(2);
intervals[name] = setInterval(watch_loop(keylist, callback), 1000/24);
}
function unwatch(name)
{
clearInterval(intervals[name]);
delete intervals[name];
}
function detach()
{
parent.removeEventListener("keydown", ev_kdown);
parent.removeEventListener("keyup", ev_kup);
}
function attach()
{
parent.addEventListener("keydown", ev_kdown);
parent.addEventListener("keyup", ev_kup);
}
function Input()
{
attach();
return {
key_down: key_down,
keys_down: keys_down_arguments,
watch: watch,
unwatch: unwatch,
clear: clear,
detach: detach
};
}
return Input();
}
Этот класс не делает все и не будет обрабатывать все возможные варианты использования. Я не библиотекарь. Но для общего интерактивного использования это должно быть хорошо.
Чтобы использовать этот класс, создайте экземпляр и укажите его на элемент, с которым вы хотите связать ввод с клавиатуры:
var input_txt = Input(document.getElementById("txt"));
input_txt.watch("print_5", function(){
txt.value += "FIVE ";
}, "Control", "5");
Что это будет делать, это присоединить новый слушатель ввода к элементу с #txt
(предположим, это текстовое поле) и установить точку наблюдения для комбинации клавиш Ctrl+5
. Когда оба параметра Ctrl
и 5
не работают, будет вызвана функция обратного вызова, которую вы передали (в данном случае, функция, которая добавляет "FIVE "
к текстовой области). Обратный вызов связан с именем print_5
, поэтому, чтобы удалить его, вы просто используете:
input_txt.unwatch("print_5");
Чтобы отсоединить input_txt
от элемента txt
:
input_txt.detach();
Таким образом, сборщик мусора может забрать объект (input_txt
), если он будет выброшен, и у вас не останется старый прослушиватель событий зомби.
Для краткости приведу краткую ссылку на API класса, представленный в стиле C / Java, чтобы вы знали, что они возвращают и какие аргументы они ожидают.
Boolean key_down (String key);
Возвращает true
, если key
выключен, в противном случае - false.
Boolean keys_down (String key1, String key2, ...);
Возвращает true
, если все клавиши key1 .. keyN
не работают, в противном случае - false.
void watch (String name, Function callback, String key1, String key2, ...);
Создает "точку наблюдения", так что нажатие всех keyN
вызовет обратный вызов
void unwatch (String name);
Удаляет указанную точку наблюдения через ее имя
void clear (void);
Стирает кеш "клавиш вниз". Эквивалент map = {}
выше
void detach (void);
Отсоединяет прослушиватели ev_kdown
и ev_kup
от родительского элемента, что позволяет безопасно избавиться от экземпляра
Обновление 2017-12-02 InВ ответ на запрос опубликовать это на github, я создал gist .
Обновление 2018-07-21 Я играл с программированием декларативного стиля дляНекоторое время, и этот способ теперь мой личный фаворит: скрипка , pastebin
Как правило, он будет работать с делами, которые вы реально хотите (ctrl, alt, shift), но если вам нужно нажать, скажем, a+w
в то же время, не составит труда объединить подходы в многоключевой поиск.
Надеюсь, что подробный ответ Мини-блог был полезен:)