Книга: JavaScript. Подробное руководство, 6-е издание

17.9. События клавиатуры

17.9. События клавиатуры

События «keydown» и «keyup» возбуждаются, когда пользователь нажимает или отпускает клавишу на клавиатуре. Они генерируются для клавиш-модификаторов, функциональных клавиш и алфавитно-цифровых клавиш. Если пользователь удерживает клавишу нажатой настолько долго, что включается режим автоповтора, будет сгенерировано множество событий «keydown», прежде чем появится событие «keyup».

Объект события, соответствующий этим событиям, имеет свойство keyCode с числовым значением, которое определяет нажатую клавишу. Для клавиш, генерирующих печатаемые символы, в общем случае свойство keyCode содержит кодовый пункт Юникода, соответствующий основному символу, изображенному на клавише. Клавиши с буквами всегда генерируют значения keyCode, соответствующие символам в верхнем регистре, независимо от состояния клавиши Shift, поскольку именно такие символы изображены на клавишах. Аналогично цифровые клавиши всегда генерируют значения keyCode, соответствующие цифровым символам, изображенным на клавишах, даже если при этом вы удерживали нажатой клавишу Shift, чтобы ввести знак препинания. Для клавиш, не соответствующих печатаемым символам, свойство keyCode будет иметь некоторое другое значение. Эти значения свойства keyCode никогда не были стандартизованы. Однако в разных броузерах они отличаются не настолько сильно, чтобы нельзя было обеспечить переносимость. Это демонстрирует пример 17.8, включающий реализацию отображения значений keyCode в имена функциональных клавиш.

Подобно объектам событий мыши, объекты событий клавиатуры имеют свойства altKey, ctrlKey, metaKey и shiftKey, которые получают значение true, если в момент возникновения события удерживалась нажатой соответствующая клавиша-модификатор.

События «keydown* и «кеуuр», а также свойство keyCode используются уже более десяти лет, но они так и не были стандартизованы. Проект стандарта «DOM Level 3 Events» стандартизует типы «keydown» и «кеуuр» событий, но не стандартизует свойство keyCode. Вместо этого он определяет новое свойство key, которое должно содержать название клавиши в виде строки. Если клавиша соответствует печатаемому символу, свойство key должно содержать этот печатаемый символ. Для функциональных клавиш свойство key должно содержать такие значения, как «F2», «Home» или «Left».

На момент написания этих строк свойство key, определяемое стандартом «DOM Level 3 Events», еще не было реализовано ни в одном из броузеров. Однако броузеры на базе механизма Webkit, Safari и Chrome определяют в объектах этих событий свойство keyIdentifier. Для функциональных клавиш, подобно свойству key, свойство keyldentifier содержит не число, а строку с именем клавиши, таким как «Shift» или «Enter». Для клавиш, соответствующих печатаемым символам, это свойство содержит менее удобное в использовании строковое представление кодового пункта Юникода символа. Например, клавише «А» соответствует значение «U+0041».

В примере 17.8 определяется класс Keymар, который отображает идентификаторы комбинаций клавиш, такие как «PageUp», «Alt_Z» и «ctrl+alt+shift+F5» в функции на языке JavaScript, вызываемые в ответ на нажатия этих комбинаций. Определения привязок клавиш передаются конструктору Кеуmap() в форме объекта JavaScript, имена свойств которого соответствуют идентификаторам комбинаций клавиш, а значения этих свойств содержат ссылки на функции-обработчики. Добавление и удаление привязок осуществляется с помощью методов bind() и unbind(). Устанавливается объект Key тар в HTML-элемент (обычно в объект Document) с помощью метода install(). При установке объекта Кеуmар в этом элементе регистрируется обработчик события «keydown». Каждый раз, когда нажимается клавиша, обработчик проверяет наличие функции, соответствующей этой комбинации. Если функция существует, она вызывается. Обработчик события «keydown» использует свойство key, определяемое стандартом «DOM Level 3 Events», если оно существует. В противном случае он пытается использовать Webkit-свойство keyldentifіег. И как запасной вариант, обработчик использует нестандартно свойство keyCode. Пример 17.8 начинается с длинного комментария, подробно описывающего работу модуля.

Пример 17.8. Класс Key тар для обработки нажатий комбинаций клавиш

/*
* Keymap.js: связывает события клавиатуры с функциями-обработчиками.
*
* Этот модуль определяет класс Keymap. Экземпляр этого класса представляет
* собой отображение идентификаторов комбинаций клавиш (определяемых ниже)
* в функции-обработчики. Объект Keymap можно установить в HTML-элемент
* для обработки событий keydown. Когда возникает это событие, объект Keymap
* использует свою карту привязок для вызова соответствующего обработчика.
*
* При создании объекта Keymap конструктору можно передать JavaScript-объект,
* представляющий начальную карту привязок. Имена свойств этого объекта должны
* соответствовать идентификаторам клавиш, а значениями должны быть функции-обработчики.
* После создания объекта Keymap в него можно добавлять новые привязки, передавая
* идентификатор клавиши и функцию-обработчик методу bind(). Имеется также возможность
* удалить привязку, передав идентификатор клавиши методу unbind().
*
* Чтобы задействовать объект Keymap, следует вызвать его метод install(), передав ему
* HTML-элемент, такой как объект document. Метод install() добавит в указанный объект
* обработчик события onkeydown. Когда этот обработчик будет вызван, он определит
* идентификатор нажатой клавиши и вызовет функцию-обработчик (если таковая имеется),
* привязанную к этому идентификатору клавиши. Один и тот же объект Keymap
* можно установить сразу в несколько HTML-элементов.
*
* Идентификаторы клавиш
*
* Идентификатор клавиши - это нечувствительная к регистру символов строка,
* представляющая клавишу, плюс любое количество удерживаемых нажатыми
* клавиш-модификаторов. Именем клавиши является основной текст, изображаемый
* на клавише. Допустимыми именами клавиш являются: "А", "7", "F2", "PageUp",
* "Left", "Backspace" и "Esc".
*
* Список имен находится в объекте Keymap.keyCodeToKeyName, внутри этого модуля.
* Они являются подмножеством имен, определяемых стандартом "DOM Level 3".
* Кроме того, этот класс будет использовать свойство key, когда оно будет реализовано.
*
* Идентификатор клавиши может также включать имена клавиш-модификаторов.
* Это имена Alt, Ctrl, Meta и Shift. Они нечувствительны к регистру символов и должны
* отделяться от имени клавиши и друг от друга пробелами или подчеркиваниями, дефисами
* или знаками +. Например: "SHIFT+A”, "Alt_F2", "meta-v" и "Ctrl alt left".
* В компьютерах Mac клавише Meta соответствует клавиша Command, а клавише Alt -
* клавиша Option. Некоторые броузеры отображают клавишу Windows в клавишу Meta.
*
* Функции-обработчики
*
* Обработчики вызываются как методы объекта document или элемента документа,
* в зависимости от того, куда был установлен объект Keymap, и им передаются
* два аргумента:
* 1) объект события keydown
* 2) идентификатор нажатой клавиши
* Значение, возвращаемое функцией, становится возвращаемым значением
* обработчика события keydown. Если функция-обработчик вернет false,
* объект Keymap прервет всплытие события и предотвратит выполнение любых
* действий по умолчанию, связанных с событием keydown.
*
* Ограничения
*
* Функцию-обработчик можно привязать не ко всем клавишам. Некоторые комбинации
* используются самой операционной системой (например, Alt-F4). А некоторые комбинации
* могут перехватываться броузером (например, Ctrl-S). Эта реализация зависит
* от особенностей броузера, ОС и региональных настроек. Вы с успехом можете
* использовать функциональные клавиши и функциональные клавиши с модификаторами,
* а также алфавитно-цифровые клавиши без модификаторов. Комбинации алфавитно-цифровых
* клавиш с модификаторами Ctrl и Alt менее надежны.
*
* Поддерживается большинство знаков препинания, кроме дефиса, для ввода которых
* не требуется удерживать клавишу Shift ('=[];',./) на клавиатурах
* со стандартной раскладкой US. Но они плохо совместимы с другими
* раскладками клавиатур, и их желательно не использовать.
*/
// Функция-конструктор
function Keymap(bindings) {
  this.map = {}; // Определить отображение идентификатор->обработчик
  if (bindings) { // Скопировать в него начальную карту привязок
    for(name in bindings) this.bind(name, bindings[name]);
  }
}
// Связывает указанный идентификатор клавиши с указанной функцией-обработчиком
Keymap.prototype.bind = function(key, func) {
  this.map[Keymap.normalize(key)] = func;
};
// Удаляет привязку для указанного идентификатора клавиши
Keymap.prototype.unbind = function(key) {
  delete this.map[Keymap.normalize(key)];
};
// Устанавливает этот объект Keymap в указанный HTML-элемент
Keymap.prototype.install = function(element) {
  var keymap = this;
  // Определить функции-обработчика события
  function handler(event) { return keymap.dispatch(event, element); }
  // Установить ее
  if (element.addEventListener)
    element.addEventListener("keydown", handler, false);
  else
    if (element.attachEvent)
      element.attachEvent("onkeydown", handler);
};
// Этот метод делегирует обработку события клавиатуры, опираясь на привязки.
Keymap.prototype.dispatch = function(event, element) {
  // Изначально нет ни имен клавиш-модификаторов, ни имени клавиши
  var modifiers = ""
  var keyname = null;
  // Сконструировать строки модификаторов в каноническом виде из символов
  // в нижнем регистре, расположив их в алфавитном порядке.
  if (event.altKey) modifiers += "alt_”;
  if (event.ctrlKey) modifiers += "Ctrl,";
  if (event.metaKey) modifiers += "meta_";
  if (event.shiftKey) modifiers += "shift.”;
  // Имя клавиши легко получить, если реализовано свойство key,
  // определяемое стандартом DOM Level 3:
  if (event.key) keyname = event.key;
  // Для получения имен функциональных клавиш в Safari и Chrome можно
  // использовать свойство keyldentifier
  else
    if (event. keyldentifier&&event. keyldentifier. substrings, 2) !== "U+")
      keyname = event.keyldentifier;
    // В противном случае можно использовать свойство keyCode и отображение код->имя ниже
    else
      keyname = Keymap.keyCodeToKeyName[event.keyCode];
  // Если имя клавиши не удалось определить, просто проигнорировать событие
  // и вернуть управление,
  if (!keyname) return;
  // Канонический идентификатор клавиши состоит из имен модификаторов
  // и имени клавиши в нижнем регистре
  var keyid = modifiers + keyname.toLowerCase();
  // Проверить, имеется ли привязка для данного идентификатора клавиши
  var handler = this.map[keyid];
  if (handler) { // Если обработчик для данной клавиши, вызвать его
                // Вызвать функцию-обработчик
    var retval = handler.call(element, event, keyid);
    // Если обработчик вернул false, отменить действия по умолчанию
    // и прервать всплытие события
    if (retval === false) {
      if (event.stopPropagation)
        event.stopPropagation(); // модель DOM
      else
        event.cancelBubble = true; // модель IE
      if (event.preventDefault)
        event.preventDefault(); // DOM
      else
        event.returnValue = false; // IE
    }
    // Вернуть значение, полученное от обработчика
    return retval;
  }
};
// Вспомогательная функция преобразования идентификатора клавиши в каноническую форму.
// Нам необходимо преобразовать идентификатор "meta” в "Ctrl", чтобы превратить
// идентификатор Meta-C в "Command-C" на компьютерах Маc и в "Ctrl-C" на всех остальных.
Keymap.normalize = function(keyid) {
  keyid = keyid.toLowerCaseO; // В нижний регистр
  var words = keyid.split(/s+|[-+_]/); // Вычленить модификаторы
  var keyname = words.pop(); // keyname - последнее слово
  keyname = Keymap.aliases[keyname] || keyname; // Это псевдоним?
  words.sort(); // Сортировать модификаторы
  words.push(keyname); // Поместить обратно
                       // нормализованное имя
  return words.join// Объединить все вместе
};
Keymap.aliases = {  // Отображение привычных псевдонимов клавиш в их
  "escape":"esc",   // "официальные" имена, используемые в DOM Level 3,
  "delete":"del",   // и отображение кодов клавиш в имена ниже.
  "return”:"enter", // Имя и значение должны состоять только из символов
  "Ctrl":"control", // нижнего регистра.
  "space":"spacebar",
  "ins":"insert"
};
// Старое свойство keyCode объекта события keydown не стандартизовано
// Но следующие значения с успехом могут использоваться в большинстве броузеров и ОС.
Keymap.keyCodeToKeyName = {
  // Клавиши со словами или стрелками на них
  8:"Backspace", 9:"Tab", 13:"Enter", 16:"Shift", 17:"Control". 18:"Alt",
  19:"Pause”, 20:"CapsLock", 27:"Esc", 32:"Spacebar", 33:"PageUp",
  34:"PageDown", 35:"End", 36:"Home", 37:"Left", 38:"Up", 39:"Right",
  40:"Down", 45:"Insert", 46:"Del",
  // Цифровые клавиши на основной клавиатуре (не на дополнительной)
  48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9".
  // Буквенные клавиши. Обратите внимание, что здесь не различаются
  // символы верхнего и нижнего регистров
  65:"А", 66:"В", 67:"С". 68:"D". 69:"Е". 70:"F", 71:"G", 72:"Н". 73:"I",
  74:"J", 75:"К", 76:"L”, 77:"М", 78:"N". 79:"О". 80:"Р", 81:"Q". 82:"R",
  83:”S", 84:"Т", 85:"U". 86:"V", 87:"W", 88:"X". 89:"Y", 90:"Z",
  // Цифровые клавиши на дополнительной клавиатуре и клавиши со знаками препинания.
  // (Не поддерживаются в Opera.)
  96:"О",97:"1",98:"2",99:"3",100:"4",101:"5",102:”6",103:"7",104:"8",
  105:"9",106:"Multiply", 107:"Add", 109:"Subtract", 110:"Decimal",
  111:"Divide",
  // Функциональные клавиши
  112:"F1", 113:"F2", 114:"F3", 115:"F4". 116:"F5", 117:"F6",
  118:”F7", 119:"F8", 120:"F9", 121:"F10", 122:"F11", 123:"F12",
  124:"F13", 125:"F14", 126:"F15", 127:”F16", 128:"F17", 129:"F18",
  130:"F19", 131:"F20", 132:"F21”, 133:"F22”, 134:"F23", 135:"F24".
  // Клавиши со знаками препинания, для ввода которых не требуется
  // удерживать нажатой клавишу Shift.
  // Дефис не может использоваться переносимым способом: FF возвращает
  // тот же код, что и для клавиши Subtract
  59:";”, 61:"=", 186:";", 187:"=", // Firefox и Opera возвращают 59,61
  188:",", 190:".", 191:"/", 192: , 219:"[", 220:"". 221:"]", 222:"'" 
};

Оглавление книги


Генерация: 0.110. Запросов К БД/Cache: 0 / 2
поделиться
Вверх Вниз