Поддержка отмены диалогового окна DHTML с escape
Представьте себе диалоговое окно DHTML со следующей разметкой:
<div id="someDialog" class="dialog">
<h2>Title of dialog</h2>
Lots: <input ...>
of: <select ...>
controls: <textarea ...>
<input type="submit" value="OK">
<input type="reset" value="Cancel">
</div>
Пользователь будет ожидать нажатия клавиши escape, чтобы отменить диалог. Само по себе это не сложно - просто добавьте обработчик события keydown в document.documentElement, чтобы проверить ev.keyCode == 27, и используйте его, чтобы закрыть самый верхний диалог на странице.
Проблема в том, что есть определенные обстоятельства, когда важно, чтобы браузер сначала увидел клавишу выхода. Например, если браузер запрашивает меню автозаполнения для <input type="text">
нажатие клавиши escape должно отменить это, а не отменить диалог. Если вы откроете выпадающее / всплывающее меню для <select>
нажатие escape должно закрывать это, а не диалог.
Как организовать обработку управляющей клавиши для окна в том и только в том случае, если браузеру не нужны какие-либо сочетания клавиш?
Редактировать: сама биржа стека имеет эту самую ошибку. Если я нажму на "Хотите получать ответы на ваши вопросы по электронной почте?" ссылка, открывающая диалоговое окно DHTML, затем вкладка в раскрывающемся меню частоты, нажмите alt-down, чтобы открыть раскрывающееся меню, затем выйдите, чтобы закрыть раскрывающееся меню, закрывается весь диалог. Этого не должно быть. В этих обстоятельствах реализация управления браузером должна сначала выбирать управляющую клавишу.
1 ответ
После некоторого приличного исследования и проб / ошибок лучшее / единственное решение здесь, кажется, создает ваши собственные пользовательские элементы управления формой.
Ниже приведена неудачная попытка решить проблему.
http://jsfiddle.net/CoryDanielson/4jBgs/10/
Вот в основном, как это работает.
Во-первых, есть activeInput
переменная, которая хранит DOMElement
ввода, на котором сосредоточен пользователь. (только если входной сигнал экранируемый)
var activeInput = false;
Чтобы заполнить эту переменную, я создал массив DOMElements, которые вы упомянули escaped
(текстовые поля с автозаполнением, выберите элементы)
var escapableElements = [];
escapableElements = escapableElements.concat(
Array.prototype.slice.call(document.getElementsByTagName('select')),
Array.prototype.slice.call(document.getElementsByTagName('input'))
//add more elements here
);
а затем перебрал массив и прикрепил eventListeners
для focus
а также blur
(потерять фокус) события. (Я включил для каждой функции в нижней части этого поста)
forEach(escapableElements, function() {
this.addEventListener('focus', registerActiveElement);
this.addEventListener('blur', deregisterActiveElement);
});
function registerActiveElement() {
if (!activeInput)
activeInput = this;
//console.log('registered'); //testing only
}
function deregisterActiveElement() {
if (activeInput)
activeInput = false;
//console.log('deregistered'); //testing only
}
После этого я подключил eventListener
для keydown
событие. Внутри я проверил, есть ли activeInput
если есть, я просто return true;
что позволит браузеру делать то, что он хочет (убежать от автозаполнения и т. д.), если нет activeInput
Я проверил, если ESC
был нажат и позвонить hide_dialog_box(event.keyCode);
Единственное отличие от пункта в вашем вопросе об обработке ESC
нажатие на клавишу, что я проверил, был ли activeInput
заранее. Если есть activeInput
, Я ничего не делал (пусть браузер обрабатывает ESC изначально), если нет activeInput
я звонил event.preventDefault()
который отменяет встроенную в браузер обработку ESC, а затем вызывает функцию hide_dialog_box(keyCode)
а потом сделал return false;
что также помогает предотвратить обработку браузером ESC
нажатие клавиши.
document.addEventListener('keydown', function(event) {
if (!activeInput) {
if (event.keyCode == 27) { //esc
event.preventDefault();
hide_dialog_box(event.keyCode);
return false;
}
} else {
return true; //if active input, let browser function
}
/*
if the browser prompts with an autocomplete menu for
<input type="text">, or options on a <select> drop down
pressing escape will cancel that, not cancel the dialog.
*/
});
Последние 2 фрагмента кода являются функцией hide_dialog_box(keyCode)
и функция, которую я написал написал, чтобы перебрать NodeList
называется escapableElements
function hide_dialog_box(keyCode) {
var dialog_box = document.getElementById('dialog_box');
dialog_box.style.display = 'none';
}
function forEach(list, callback) {
for (var i = 0; i < list.length; i++)
{
//calls the callback function, but places list[i] as the 'this'
callback.call(list[i]);
}
}