JavaScript事件处理和流程控制

5
我将尝试构建一个根据提供的输入加载的网页。基本上,我在JavaScript中遇到了一些事件处理问题。如果我想在继续显示下一个对象之前等待特定的键盘输入,从Python开始,我会创建一个while循环并在其中放置一个键盘监听器。
Python:
def getInput():
  while 1:
    for event in pygame.event.get(): #returns a list of events from the keyboard/mouse
      if event.type == KEYDOWN:
        if event.key == "enter": # for example
          do function()
          return
        elif event.key == "up":
          do function2()
          continue
        else: continue # for clarity

在尝试在DOM / javascript中实现此操作时,似乎会使页面崩溃(我想是由于While循环),但我认为这是因为我的事件处理编写不好。此外,“element.onkeydown = function”的注册事件处理程序对我来说很难理解,并且setInterval(foo(),interval] 也没有给我带来太多成功。
基本上,我想要一个“监听”循环来执行某个按键X的行为,但在按下按键Y时终止。
7个回答

8
在JavaScript中,你放弃了对主循环的控制。浏览器运行主循环并在事件或超时/间隔发生时回调到你的代码。你必须处理事件然后返回,以便浏览器可以继续做其他事情,触发事件等等。
所以你不能有一个“监听”循环。浏览器为你完成了这个过程,给你事件并让你处理它,但一旦你处理完事件,你必须返回。你不能回到不同的循环中。这意味着你不能编写逐步过程化的代码;如果你有在事件调用之间持久存在的状态,你必须将其存储,例如在变量中。
这种方法行不通:
<input type="text" readonly="readonly" value="" id="status" />

var s= document.getElementById('status');
s.value= 'Press A now';
while (true) {
    var e= eventLoop.nextKeyEvent(); // THERE IS NO SUCH THING AS THIS
    if (e.which=='a')
        break
}
s.value= 'Press Y or N';
while (true) {
    var e= eventLoop.nextKeyEvent();
    if (e.which=='y') ...

需要一步步地将代码反转,使得浏览器调用您的代码,而不是您调用浏览器的代码:

var state= 0;
function keypressed(event) {
    var key= String.fromCharCode(event? event.which : window.event.keyCode); // IE compatibility
    switch (state) {
        case 0:
            if (key=='a') {
                s.value= 'Press Y or N';
                state++;
            }
            break;
        case 1:
            if (key=='y') ...
            break;
    }
}

s.value= 'Press A now';
document.onkeypress= keypressed;

您还可以使用嵌套的匿名函数使代码看起来更加线性化,并清理一些状态问题:

s.value= 'Press A now';
document.onkeypress= function(event) {
    var key= String.fromCharCode(event? event.which : window.event.keyCode);
    if (key=='a') {
        s.value= 'Press Y or N';
        document.onkeypress= function(event) {
            var key= String.fromCharCode(event? event.which : window.event.keyCode);
            if (key=='y') ...
        };
    }
};

1
谢谢Bobince。你对逐步过程代码解决我的问题的评论非常好。但愿我有足够的声望来为你点赞。 - intelligencer

1

在JavaScript中,你不应该使用这样的循环。基本上,你不希望阻塞浏览器完成其工作。因此,你要使用事件(onkeyup/down)。

此外,如果你想等待一会儿并在发生某些事情后继续执行,而不是使用循环,你应该使用 setTimeout。

你可以像这样做:

<html>
<script>
var dataToLoad = new Array('data1', 'data2', 'data3' );
var pos = 0;
function continueData(ev) {
  // do whatever checks you need about key
  var ele = document.getElementById("mydata");
  if (pos < dataToLoad.length)
  {
     ele.appendChild(document.createTextNode(dataToLoad[pos]));
     pos++;
  }
}
</script>
<body onkeyup="continueData()"><div id="mydata"></div></body></html>

每次释放一个键时,下一个数据字段就会被追加。

如果您需要在处理程序中使用事件对象,那么 ev 应该真正成为 event,因为这是传递给函数的名称。当然,IE 是“特殊”的,并将事件对象分配给全局 window.event 而不是将其传递给 event 下的事件处理程序函数。 - kangax
true - 你显然需要调整代码以响应不同的按键按下/释放事件。我试图演示如何解决while循环问题。 - Niko

1
为了更容易地实现事件处理,我建议您使用类库,比如 PrototypeJquery (请注意,这两个链接会带您到它们各自的事件处理文档)。
为了使用它们,您需要记住以下三点:
  • 您要观察哪个 DOM 元素
  • 您要捕获哪个事件
  • 事件将触发什么操作
这三个点是相互包含的,这意味着在编写代码时,您需要同时考虑这三个方面。
因此,有了这些想法,使用 Prototype,您可以这样做:
Event.observe($('id_of_the_element_to_observe'), 'keypress', function(ev) {
  // the argument ev is the event object that has some useful information such
  // as which keycode was pressed.
  code_to_run;
});

这里是一个更有用的示例代码,一个字符计数器(例如在Twitter中找到的那种,但肯定不太可靠 ;)):

var CharacterCounter = Class.create({

  initialize: function(input, counter, max_chars) {
    this.input = input;
    this.counter = counter;
    this.max_chars = max_chars;
    Event.observe(this.input, 'keypress', this.keyPressHandler.bind(this));
    Event.observe(this.input, 'keyup', this.keyUpHandler.bind(this));
  },

  keyUpHandler: function() {
    words_left = this.max_chars - $F(this.input).length;
    this.counter.innerHTML = words_left;
  },

  keyPressHandler: function(e) {
    words_left = this.max_chars - $F(this.input).length;
    if (words_left <= 0 && this.allowedChars(e.keyCode)) {
      e.stop();
    }
  },

  allowedChars: function(keycode) {
    // 8: backspace, 37-40: arrow keys, 46: delete
    allowed_keycodes = [ 8, 37, 38, 39, 40, 46 ];
    if (allowed_keycodes.include(keycode)) {
      return false;
    }
    return true
  }

});

0
任何好的浏览器在遇到运行时间过长的脚本时都会崩溃。这是为了防止恶意网站锁定客户端应用程序。
在javascript中,您不能有无限循环。相反,将事件侦听器附加到窗口,并在处理程序中执行处理(将其视为中断而不是轮询)。
例如:
function addEventSimple(obj,evt,fn) {
    if (obj.addEventListener)
        obj.addEventListener(evt,fn,false);
    else if (obj.attachEvent)
        obj.attachEvent('on'+evt,fn);
} // method pulled from quirksmode.org for cross-browser compatibility

addEventSimple(window, "keydown", function(e) {
    // check keys
});

0
document.onkeydown = function(e) {
  //do what you need to do
}

这就是 JavaScript 所需要的全部。你不需要循环等待事件发生,每当事件发生时,该函数将被调用,进而可以调用其他函数并执行必要的操作。可以将其视为,与其让你等待所需的事件发生,你正在寻找的事件会在发生时通知你。

0
你可以像这样将事件监听器附加到窗口对象
window.captureEvents(Event.KEYPRESS);
window.onkeypress = output;
function output(event) {
  alert("you pressed" + event.which);
}

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接