2010-06-29 83 views
10

按下游戏中的空格键将进行角色拍摄,当显示确认框时按空格键将使该框消失,按高分表格中的空格键将在输入框中添加空格。在这个例子中,同一个键有几个事件,但是每次只有一个事件被触发。在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 */ }; 

但这是程序设计的一个非常混乱的方式,因为具体行动,在空格键的处理函数,这使得维修难混在一起。

处理一个键的多个事件的最佳方式是什么,以便这些事件只在某些情况下才会被触发?

回答

4

函数是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"; 

所有这些情况将由一个函数处理。如果您想要扩展另一个案例,您只需将其他功能添加到操作对象。注意整个事物是如何封装到一个对象中的。

+0

我想这是最好的答案,谢谢!这个想法很好,我一定会使用它。我也会添加一个方法'addState(name,fn)',它使这个对象完成。感谢:] – Harmen 2010-06-29 20:30:58

+0

但仍然远离处理这个问题的“最好的方式”。一个托管堆栈的处理程序更方便!由于这个问题很过时,如果有人有兴趣,我可以开发。 – Pierre 2012-12-21 09:37:56

0

将事件监听器附加到个别元素而不是整个文档。

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库来处理事件。

+1

这不是一个坏主意,但一般问题是,虽然'文档'总是有焦点,'#游戏'赢'除非你故意将焦点移动到其内部(如果用户点击并返回窗口,则可能会失去焦点)。 – bobince 2010-06-29 18:30:46

+0

嗯是的,我明白你在说什么。 Web App的心态在这里。这可能与植入拖拽元素或调整元素大小的功能类似,“mousemove”事件侦听器必须附加到窗口或文档上,否则一旦光标没有在其位置更新之前悬停在该元素上方,事件就会停止触发事件功能。好啊 – MooGoo 2010-06-29 18:47:13

0

有几种方法,通常涉及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; 
} 
+0

除了当确认框没有焦点时发生什么(例如,未设置焦点,或者在用户点击游戏区域时未重置焦点),在这种情况下确认框不再捕捉输入 – 2010-06-29 18:31:52

+0

顺便说一句,是否有任何理由不使用默认的运算符:'event = event || window.event'? – 2010-06-29 18:35:23

+0

这就是通常想要的;我不确定OP后的行为。如果关键事件的来源不重要,并且只有时间有关系,那么唯一的方法就是外部变量。 – bobince 2010-06-29 18:36:02

0

您可以根据需要添加和删除事件侦听器。

让我们假设你使用的是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按钮触发,等等......

相关问题