Каков наилучший способ обработки нескольких ключевых событий в Javascript?
Нажатие пробела в игре сделает стрельбу персонажа, нажав клавишу пробела, когда появится окно подтверждения, это окно исчезнет, и нажатие пробела в форме рекорда добавит пробел в поле ввода. В этом примере есть несколько событий для одного и того же ключа, но только один из них запускается за раз.
Есть ли общий (или специфический для Javascript) метод или способ программирования для добавления событий к определенному ключу, поэтому они выполняются только при определенных обстоятельствах?
Конечно, это можно сделать следующим образом:
var inGame = true|false;
var inConfirmationBox = true|false;
function spaceBarHandler(){
if(inGame){ /*shoot*/}
else if(inConfirmationBox){ /*remove box*/}
}
document.onkeydown = function(){ /* call space bar handler if space bar was pressed */ };
Но это очень запутанный способ программирования, поскольку конкретные действия смешиваются вместе в функции обработчика пробела, что затрудняет техническое обслуживание.
Каков наилучший способ обработки нескольких событий для одного ключа, чтобы эти события запускались только при определенных обстоятельствах?
Ответы
Ответ 1
Функции являются первоклассными объектами в javascript, что делает их действительно мощными. Из-за этого ваша проблема может быть решена очень элегантно.
// the whole thing can be encapsulated
// into an object actually
function spaceBarHandler() {
var state = spaceBarHandler.state;
var actions = spaceBarHandler.actions;
// execute function if exists
if (actions[state]) {
actions[state]();
}
}
// spaceBar actions
spaceBarHandler.actions = {
shoot: function() {
// bang bang
},
removeBox: function() {
// do it...
}
};
// change current state from outside
// we are in the game
spaceBarHandler.state = "shoot";
// change current state from outside
// confirmation box is shown
spaceBarHandler.state = "removeBox";
Все эти случаи будут обрабатываться одной функцией. Если вы хотите продолжить с другим случаем, вы просто добавляете еще одну функцию в объект action. Обратите внимание, как все это инкапсулировано в один объект.
Ответ 2
Прикрепите прослушиватели событий к отдельным элементам вместо всего документа.
document.getElementById('highscore').onkeypress = function(keyEvent) {
if (is_spacebar(keyEvent)) //Do something...
};
document.getElementById('game').onkeypress = function(keyEvent) {
if (is_spacebar(keyEvent)) //Do something else...
};
Это упрощенный пример. Вероятно, вам придется иметь дело с пузырьками событий, которые можно контролировать при использовании addEventListener() для присоединения функций к событиям. Учитывая проблемы совместимости с браузером (IE), связанные с этим, для обработки событий следует использовать некоторую библиотеку JS.
Ответ 3
Существует несколько способов, обычно связанных с разветвлением кода для специальной модели событий IE.
Один из способов - остановить нажатия клавиш, которые обрабатываются дальше от пузырьков до обработчика ключа документа:
confirmationbox.onkeydown = function(event) {
if (event === undefined) event = window.event;
// do something with event.keyCode
if ('stopPropagation' in event) // standards browsers
event.stopPropagation();
else if ('cancelBubble' in event) // IE before version 9
event.cancelBubble = true;
};
document.onkeydown = ... // will not be called for keydowns inside confirmationbox
Другой способ - проверить целевой элемент события, чтобы увидеть, находится ли он в поле:
document.onkeydown = function(event) {
if (event === undefined) event = window.event;
var target = 'target' in event ? event.target : event.srcElement; // srcElement is for IE<9
if (target === containerbox || isDescendantOf(target, containerbox) {
// do containerbox stuff
} else {
// do other stuff
}
};
function isDescendantOf(element, ancestor) {
while (element = element.parentNode)
if (element === ancestor)
return true;
return false;
}
Ответ 4
вместо этого вы можете добавить и удалить прослушиватель событий по мере необходимости.
предположим, что вы используете фреймворк javascript (если нет, то вам, вероятно, стоит подумать о количестве JS-кода, участвующего в такой игре)
с использованием PrototypeJS:
когда игра начинается,
document.observe("keydown",shootHandler());
когда создается окно сообщения,
function createBox(text) {
...snip
document.observe("keydown",closeBox());
document.fire("game:pause");
}
и, например,
var paused = false;
function shoothandler() {
if (!paused) {
alert("pew! pew!");
}
}
function closeBox() {
$('messagebox').remove();
document.fire("game:unpaused");
document.stopObserving("keydown",closeBox());
}
document.observe("game:paused", function() { paused = true;});
document.observe("game:unpaused", function() { paused = false;});
document.observe("game:over", function() { document.stopObserving("keydown",shootHandler());});
Я не включил экран с высоким рейтингом, но теория одинаков.
Как вы можете видеть, я также использовал пользовательские события для уведомления о состоянии паузы. То же самое событие также может быть огнем кнопкой puase в интерфейсе и т.д.