Каков наилучший способ обработки нескольких ключевых событий в 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 в интерфейсе и т.д.