【发布时间】:2017-10-16 02:00:02
【问题描述】:
这显然是SSCCE。
因此,我们的任务是编写导弹发射控制系统的前端。我们选择 Spartan 布局,因为这非常严重:只有一个文本输入框和一个用于输入代码的按钮:
为了安全起见,点击“确定”按钮后,我们将显示一个对话框,要求用户确认:
作为可用性的最后润色,我们为 Enter 按钮添加了一个按键侦听器,该侦听器也将导致单击“确定”按钮(使用 $.trigger())。
不幸的是,确认对话框仅在用户单击“确定”按钮时显示,而在按 Enter 时不显示。当我们点击 Enter 时,对话框根本不会出现。
更糟糕的是,在添加了一些调试消息后,似乎对话框确实显示了几分之一毫秒,然后由于某种原因单击了“是”按钮。所以当Enter被击中时,导弹发射立即被确认!
小提琴here.
代码如下:
function inputKeyListener(evt) {
console.log('key listener - triggered key code is: ' + evt.keyCode);
if (evt.keyCode === $.ui.keyCode.ENTER) {
evt.stopPropagation();
$('#missile-launch-button').click(); // Directly calling confirm() doesn't work either
}
}
function missileLaunchButtonClickHandler(e) {
e.stopPropagation();
confirm();
}
function confirm() {
var launchCode = $('#missile-launch-code-input').val();
const dialog = $('#missile-launch-confirmation-modal');
dialog.dialog({
closeOnEscape: false,
dialogClass: 'no-close',
open: function(event, ui) {
console.log('confirm :: open is called');
},
close: function() {
console.log('confirm :: close is called');
},
resizable: false,
height: "auto",
width: 400,
modal: true,
buttons: {
"Yeap": function() {
console.log('Confirmation button was clicked');
$(this).dialog("close");
console.log('missile launch with code [' + launchCode + '] was confirmed!');
},
"Maybe not just yet": function(ev) {
console.log('Abort button was clicked');
$(this).dialog("close");
console.log('Armageddon was averted');
}
}
});
dialog.dialog('open');
console.log('by this time the dialog should be displayed');
}
$('#missile-launch-confirmation-modal').dialog({
autoOpen: false
});
$('#missile-launch-button').click(missileLaunchButtonClickHandler);
$(document).on('keydown', inputKeyListener);
<link rel='stylesheet' href='https://code.jquery.com/ui/1.11.4/themes/vader/jquery-ui.css'>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<div id='missile-launch-confirmation-modal' title='Confirm missile launch' </div>
<span class="ui-icon ui-icon-alert" style="float:left; margin:12px 12px 20px 0;"></span> Are you sure you want to unleash nuclear Armageddon?
</div>
</div>
<div>
<div>
<div>Enter missile launch code:</div>
<div>
<input id='missile-launch-code-input' type='text' autofocus/>
</div>
<div>
<button id='missile-launch-button' type='button'>OK</button>
</div>
</div>
</div>
更新
在上面的代码中,inputKeyListener 绑定到文档上的keydown。将其更严格地绑定到文本输入上的 keydown,如下所示:
$('#missile-launch-code-input').on('keydown', inputKeyListener);
…导致完全相同的行为。
更新二
This answer 建议 stopPropagation 在这里无效,因为“事件冒泡在这里并没有真正发挥作用”并解释说 preventDefault 应该用于“[停止]到达其他页面元素(即该按钮)的关键事件”。我对这两个陈述放在一起有点困惑。我认为stopPropagation 正是用来阻止“关键事件到达其他页面元素”。此外,还有两点令人困惑。
混淆的第一点是确认对话框div不是文本输入div的父DOM元素,所以不清楚文本输入div处的键盘事件是如何被sibling(不是 parent)DOM 元素。我认为这实际上是stopPropagation 无效的原因,但我仍然不清楚为什么(不管stopPropagation)事件到达位于兄弟div 中的确认对话框按钮。
第二个混淆点是,如果我们在“Yeap”按钮函数处理程序中记录我们捕获的事件,例如像这样:
buttons: {
"Yeap": function(ev) {
console.log(ev);
……我们实际上在控制台中看到的是:
…所以它是一个鼠标事件,而不是确认对话框的键盘事件。鉴于(在一个简单的点击 Enter 的场景中)我们生成的唯一 mouse 事件是在inputKeyListener:
$('#missile-launch-button').click();
…这意味着是这个事件导致了对话框的确认,而不是我们通过点击 Enter
得到的键盘事件【问题讨论】:
-
我刚刚将
evt.stopPropagation();替换为evt.preventDefault();,它按预期工作。 -
@GauravBhor 是的,但为什么呢?
preventDefault()用于防止“浏览器默认行为”。在这种情况下,“浏览器的默认行为”如何将鼠标事件发送到不相关的div元素?
标签: javascript jquery jquery-ui jquery-ui-dialog