Поддержка отмены диалогового окна 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]);
    }
}
Другие вопросы по тегам