3

想象一个带有以下标记的 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>

用户会期望点击退出来取消对话框。这本身并不难——只需将 keydown 事件处理程序添加到 document.documentElement 以检查 ev.keyCode == 27,并使用它来关闭页面上最顶部的对话框。

问题是——在某些情况下,浏览器首先看到转义键很重要。例如,如果浏览器提示自动完成菜单<input type="text">,按转义键应该取消,而不是取消对话框。如果您调出 a 的下拉/弹出菜单<select>,按 Escape 应该关闭它,而不是对话框。

当且仅当浏览器不需要转义键时,您如何安排处理窗口的转义键?


编辑: Stack Exchange 本身就有这个错误。如果我单击“您希望通过电子邮件将您的问题的回复发送给您吗?” 链接,打开一个 DHTML 对话框,然后 tab 到频率下拉菜单,按 alt-down 打开下拉菜单,然后退出以关闭下拉菜单,整个对话框关闭。这不应该发生。在这些情况下,浏览器的控制实现应该首先选择转义键。

4

1 回答 1

2

经过一些体面的研究和试验/错误,这里最好/唯一的解决方案似乎是创建自己的自定义表单控件。


以下是解决问题的失败尝试。

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
);

然后循环遍历数组并附加到eventListenersfocusblur失去焦点)事件。(我在这篇文章的底部包含了每个函数)

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]);
    }
}
于 2012-04-16T18:44:57.750 回答