Обработка событий Javascript и управление потоком

Я пытаюсь создать веб-страницу, которая загружается в зависимости от предоставленного ввода. В основном у меня возникают проблемы с обработкой событий в javascript. Исходя из Python, если бы я хотел дождаться определенного ввода с клавиатуры, прежде чем переходить к следующему объекту для отображения, я бы создал цикл while и поместил в него прослушиватель клавиш.

Питон:

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.


person intelligencer    schedule 21.08.2009    source источник


Ответы (7)


В 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') ...
        };
    }
};
person bobince    schedule 21.08.2009
comment
Спасибо бобинка. Ваш комментарий о том, какой пошаговый процедурный код очень хорошо решил мою проблему. Жаль, что у меня не было достаточно репутации, чтобы проголосовать за вас. - person intelligencer; 21.08.2009

вы не должны использовать такие циклы в 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>

каждый раз, когда клавиша отпускается, добавляется следующее поле данных

person Niko    schedule 21.08.2009
comment
Если вам нужно использовать объект события в обработчике, этот ev действительно должен быть event, поскольку это имя, под которым оно передается функции. IE, конечно, особенный и назначает объект события глобальному window.event, а не передает его функции обработчика событий в event. - person kangax; 21.08.2009
comment
true - вам, очевидно, нужно настроить код, чтобы он действовал на разные нажатия/отпускания клавиш. Я пытался продемонстрировать, как решить проблему с циклом while - person Niko; 21.08.2009

Для более простой реализации обработки событий я рекомендую вам использовать библиотеку, такую ​​как Prototype или Jquery (Обратите внимание, что обе ссылки ведут к соответствующей документации по обработке событий.

Чтобы использовать их, вы должны иметь в виду 3 вещи:

  • Какой элемент 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;
});

Вот код более полезного примера, CharacterCounter (например, тот, который можно найти в 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
  }

});
person Lonecat    schedule 21.08.2009

Любой хороший браузер рухнет, когда встретит скрипт, который выполняется слишком долго. Это делается для того, чтобы вредоносные веб-сайты не блокировали клиентское приложение.

Вы не можете иметь бесконечный цикл в 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
});
person Joel    schedule 21.08.2009

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

Это все, что нужно в javascript. Вам не нужно зацикливаться, чтобы дождаться события, всякий раз, когда событие происходит, будет вызываться функция, которая, в свою очередь, может вызывать другие функции, делать все, что нужно сделать. Думайте об этом так, что вместо того, чтобы ждать, пока событие, которое вы ищете, произойдет, событие, которое вы ищете, сообщит вам, когда оно произойдет.

person TJ L    schedule 21.08.2009

вы можете прикрепить прослушиватель событий к объекту окна, как это

window.captureEvents(Event.KEYPRESS);
window.onkeypress = output;
function output(event) {
  alert("you pressed" + event.which);
}
person stefita    schedule 21.08.2009

Проверьте прослушиватель ключей YUI

http://developer.yahoo.com/yui/docs/YAHOO.util.KeyListener.html

используя ключевой прослушиватель, YUI заботится о захвате любых событий. В javascript почти никогда не будет случая, когда вы должны ждать в цикле while, чтобы что-то произошло.

Если вам нужны примеры того, как работает обработка событий, ознакомьтесь с этими страницами.

http://developer.yahoo.com/yui/examples/event/eventsimple.html

person Zoidberg    schedule 21.08.2009