Создание собственных обработчиков клавиатурных сообщений в JavaScript

Если web-приложение активно использует всплывающие окна как аналог диалогов настольных приложений, то правильным представляется сделать поведение всплывающих окон web-приложений максимально похожим на поведение обычных диалоговых окон, к которому все давно привыкли. В частности, сделать похожими реакцию на нажатие управляющих клавиш, таких как Esc, Tab, Enter.

Что бы не усложнять, рассмотрим простую задачу — закрытие всплывающего окна окна по нажатию на Esc1. Нажатие на Esc симулирует нажатие на Cancel, что, как правило, приводит к закрытию диалога. Помимо этого, из очевидных соображений, хочется реализовать обработку клавиатурного ввода в одном скрипте. 

Обработка клавиатурных сообщений в JavaScript

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

 Далее возможны варианты, реализовать все непосредственно в функции-обработчике, или, в зависимости от типа клавиши, вызывать конкретную функцию. Итак, есть функция-обработчик клавиатурных сообщений.

function onKeyboard(event)
{
if ( event.keyCode == 27) //Escape
    window.close(); // Обработка нажатия других клавиш.
}

Обработчик клавиатурных событий onKeyPress определен у объекта document, (автоматически, у всех объектов являющихся его потомками).

Важное замечание, у window, родительского для всех объектов DOM модели кроме navigator, обработчик клавиатурных событий не определен. В этом можно убедиться посмотрев свойства объектов window и document.


Иерархия объектов HTML DOM. Взято с сайта Netscape.

 

Обработка клавиатурных сообщений в обычном окне

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

вот так
<script language = "javascript">
<!--
window.document.onKeyPress = onKeyboard(event);
//-->
</script>

либо вот так
<body class="editEvent" onKeyPress= "return onKeyboard(event);">

Обработка клавиатурных сообщений в frameset-окне

Клавиатурные сообщения окна, содержащего фреймы, т.е. окна не содержащего документ, обрабатываются функцией обработчиком документа активного фрейма.

Соответственно, обработать клавиатурные сообщения можно только в конкретном фрейме и использовать вышеприведенный скрипт не получиться, так как window в данном случае не окно, а активный фрейм и закрыть его нельзя.

Решением может быть вариант, когда в каждый фрейм импортируется скрипт, по нажатию на Esc закрывающий родительское окно фрейма. Однако данном случае будет два обработчика клавиатурных сообщений, реализующих одинаковое, по-сути, поведение. Один для обычных и один для окон, содержащих фреймы.

Так же можно модифицировать существующий скрипт, передавая ему DOM объект окна в качестве параметра. Однако в этом случае вся работа будет производиться в контексте переданного в функцию параметра, а не стандартного для JavaScript DOM объекта window. Что во-первых, может привести к ошибкам. Во-вторых, если до этого в приложении не было ни одного окна содержащего фреймы и обработка клавиатурных сообщений была реализована стандартным способом, смена сигнатуры функции-обработчика потребует внесения изменения в код всех страниц, его использующих, и, чему многие не уделяют должного внимания, тестирование приложения.

Общее решение

В коде окна-контейнера определяется глобальная переменная, например keyPressed, которой присваивается значение стандартной функции обработчика onKeyboard. Эта переменная имеет тип Function и является новым свойством объекта window, то есть доступна из каждого фрейма как window.parent.keyPressed.

<script language="JavaScript" src = "keyboard.js"></script>
<script language="JavaScript">
<!--
window.keyPressed = onKeyboard;
//-->
</script>

В дочерних окнах-фреймах декларируется стандартный обработчик onKeyPress из которого, в свою очередь, вызывается обработчик окна контейнера.

<body onKeyPress= "if (parent) parent.keyPressed(event);">

Вот, собственно и все.

Откроется новое окно.

1) Задача не в том что бы закрыть окно, а в том что бы определить один разработчик сообщения для всех типов окон. Закрытие окна — для простоты примера.