神秘的鼠标事件关闭了jQuery UI对话框

这显然是一个SSCCE 。

所以我们的任务是编写导弹发射控制系统的前端。 我们选择Spartan布局,因为这是非常严重的:只需一个文本输入框和一个输入代码的按钮:

在此处输入图像描述

为安全起见,点击“确定”按钮后,我们将显示一个对话框,要求用户确认:

在此处输入图像描述

作为可用性完成触摸,我们为Enter按钮添加一个键监听器,这也将导致单击“确定”按钮(使用$.trigger() )。

不幸的是,确认对话框仅在用户单击“确定”按钮时显示,但在按Enter键时不显示。 当我们按Enter键时 ,对话框根本不显示。

最糟糕的是,在添加一些调试消息后,似乎对话框确实显示了几分之一毫秒,然后由于某种原因点击了“Yeap”按钮。 因此,当Enter被击中时,导弹发射立即被确认!

在这里小提琴。

代码如下:

 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); 
    <div id='missile-launch-confirmation-modal' title='Confirm missile launch' 
Are you sure you want to unleash nuclear Armageddon?
Enter missile launch code:

更新

在上面的代码中, inputKeyListener绑定到文档上的keydown 。 将它更窄地绑定到文本输入上的keydown ,如:

 $('#missile-launch-code-input').on('keydown', inputKeyListener); 

…导致完全相同的行为。

更新II

这个答案表明stopPropagation在这里是无效的,因为“ 事件冒泡在这里没有真正起作用 ”,并解释了preventDefault应该用于“ [停止]键事件到达其他页面元素(即那个按钮) ”。 我对这两个一起的陈述有点困惑。 我认为stopPropagation 正是用来阻止“ 关键事件到达其他页面元素 ”的东西。 此外还有两个混乱点。

第一个混淆点是确认对话框div不是文本输入div的父DOM元素,因此不清楚文本输入div的键盘事件是如何被兄弟 (而不是 )DOM元素截获的。 我认为这实际上是stopPropagation无效的原因,但我仍然不清楚为什么(不管stopPropagation )事件是否到达了兄弟div的确认对话框按钮。

第二个困惑点是,如果我们记录我们在“Yeap”按钮函数处理程序中捕获的事件,例如:

 buttons: { "Yeap": function(ev) { console.log(ev); 

…我们实际上在控制台中看到的是:

在此处输入图像描述

…所以这是一个鼠标事件,而不是确认对话框的键盘事件。 鉴于(在一个简单命中Enter的场景中)我们生成的唯一鼠标事件在inputKeyListener

 $('#missile-launch-button').click(); 

…这意味着正是这个事件导致对话框的确认,而不是我们通过按Enter键获得的键盘事件

这似乎是jQuery UI对它自身的好处略微有用的情况:当一个dialog打开时,它将第一个按钮置于焦点内,正好赶上“enter”键事件触发按钮(这是当按钮处于焦点时用户点击“输入”时的浏览器默认行为。)

inputKeyListener使用preventDefault inputKeyListener阻止键事件到达其他页面元素(即该按钮)。 stopPropagation是无害的,但在inputKeyListenermissileLaunchButtonClickHandler没有任何有用的效果,因为事件冒泡在这里并没有真正起作用。

这是一个没有preventDefault或stopPropagation的演示,并且包含一个虚拟按钮以无害地捕获自动对焦,只是为了确认这是发生了什么:

 function inputKeyListener(evt) { console.log('key listener - triggered key code is: ' + evt.keyCode); if (evt.keyCode === $.ui.keyCode.ENTER) { // $('#missile-launch-button').click(); // Directly calling confirm() doesn't work either confirm(); // Does too! } } function missileLaunchButtonClickHandler(e) { 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: { "Hmmmm": function() { console.log('First button inside the dialog was clicked.'); }, "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); 
    
Are you sure you want to unleash nuclear Armageddon?
Enter missile launch code:

首先,您需要在inputKeyListener函数中调用missileLaunchButtonClickHandler

在您需要向missileLaunchButtonClickHandler函数添加“preventDefault”之后,因为当您按Enter时对话框会自动关闭。 preventDefault避免对话框自动关闭。

missileLaunchButtonClickHandler函数更改为:

 function missileLaunchButtonClickHandler(e) { //e.stopPropagation(); e.preventDefault(); confirm(); } 

并将inputKeyListener修改为:

 function inputKeyListener (evt) { console.log('key listener - triggered key code is: '+evt.keyCode); if (evt.keyCode === $.ui.keyCode.ENTER) { evt.stopPropagation(); missileLaunchButtonClickHandler(evt); $('#missile-launch-button').click(); // directly calling confirm() doesn't work either } } 
Interesting Posts