想象一个带有以下标记的 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 应该关闭它,而不是对话框。
当且仅当浏览器不需要为某些东西使用转义键时,您如何安排处理窗口的转义键?
编辑:Stack Exchange 本身就有这个错误。如果我单击“您希望通过电子邮件将对您的问题的答复发送给您吗?”链接,打开一个 DHTML 对话框,然后选择频率下拉菜单,按 alt-down 打开下拉菜单,然后退出关闭下拉菜单,整个对话框关闭。这不应该发生。在这些情况下,浏览器的控件实现应该首先选择转义键。
最佳答案
经过一些体面的研究和试错,这里最好/唯一的解决方案似乎是创建您自己的自定义表单控件。
以下是解决问题失败的尝试。
http://jsfiddle.net/CoryDanielson/4jBgs/10/
基本原理如下。
首先,有一个 activeInput
变量,它存储用户关注的输入的 DOMElement
。 (仅当输入可转义时)
var activeInput = false;
为了填充此变量,我创建了一个您提到的可以转义
的 DOMElement 数组(具有自动完成功能的文本框,选择元素)
var escapableElements = [];
escapableElements = escapableElements.concat(
Array.prototype.slice.call(document.getElementsByTagName('select')),
Array.prototype.slice.call(document.getElementsByTagName('input'))
//add more elements here
);
然后遍历数组并为 focus
和 blur
(失去焦点)事件附加 eventListeners
。 (我在这篇文章的底部包含了每个函数)
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
}
之后,我为 keydown
事件连接了一个 eventListener
。在其中,我检查是否有 activeInput
如果有,我只是 return true;
这会让浏览器做它想做的事(从自动完成中逃脱,等)如果没有 activeInput
,我检查是否按下了 ESC
并调用 hide_dialog_box(event.keyCode);
与您问题中有关处理 ESC
按键的段落的唯一区别是,我事先检查了是否有 activeInput
。如果有 activeInput
,我什么都不做(让浏览器本地处理 ESC)如果没有 activeInput
我调用了 event.preventDefault()
将取消浏览器对 ESC 的 native 处理,然后调用函数 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]);
}
}
关于javascript - 支持用escape取消DHTML对话框,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10178793/