这显然是SSCCE。
因此我们的任务是编写导弹发射控制系统的前端。我们选择Spartan布局,因为这是非常严重的:只需一个文本输入框和一个输入代码的按钮:
出于安全考虑,点击“确定”按钮后,我们将显示一个对话框,要求用户确认:
作为可用性完成触摸,我们为 Enter 按钮添加一个关键监听器,这也将导致单击“确定”按钮(使用$.trigger()
)。
不幸的是,确认对话框仅在用户点击“确定”按钮时显示,但在点击 Enter 时不显示。当我们点击 Enter 时,对话框根本不会出现。
最糟糕的是,在添加一些调试消息之后,看起来对话框确实显示了几分之一毫秒,然后由于某种原因点击了“Yeap”按钮。因此当 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元素,因此不清楚文本输入中的键盘事件{{1}被兄弟(不是父)DOM元素拦截。我认为这实际上是div
无效的原因,但我仍然不清楚为什么(无论stopPropagation
)事件到达确认对话框按钮,该按钮位于兄弟stopPropagation
中
第二个困惑点是,如果我们记录我们在“Yeap”按钮功能处理程序中捕获的事件,例如像这样:
div
...我们实际上在控制台中看到的是:
...所以它是鼠标事件,而不是确认对话框的键盘事件。鉴于(在一个简单命中 Enter 的情况下)我们生成的唯一鼠标事件位于buttons: {
"Yeap": function(ev) {
console.log(ev);
:
inputKeyListener
...这意味着正是这个事件导致对话框的确认,而不是我们通过点击 Enter 获得的键盘事件
答案 0 :(得分:6)
这似乎是jQuery UI对自己的好处略微有用的情况:当dialog
打开时,它会将第一个按钮置于焦点内,正好赶上&#34;输入& #34;用于触发按钮的键事件(当用户点击&#34时,这是浏览器的默认行为;当按钮处于焦点时,输入&#34;)
在preventDefault
中使用inputKeyListener
会阻止关键事件到达其他页面元素(即该按钮)。 stopPropagation
是无害的,但在inputKeyListener
或missileLaunchButtonClickHandler
中没有任何有用的效果,因为事件冒泡在这里并没有真正起作用。
这是一个没有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);
&#13;
<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>
&#13;
为了扩展这一点,每个&#34;更新II&#34;:stopPropagation
可防止事件冒泡到父DOM节点。通常,例如,单击事件从直接通过每个父节点单击的节点向上冒泡。
stopPropagation
与此无关的原因是因为dialog
不是输入元素的父级:事件冒泡不会达到dialog
。所以没有理由阻止事件冒泡stopPropagation
,因为它无论如何也不会触发任何有意义的事情。
相反,event.preventDefault
停止的事件与DOM结构无关 - 如果父母,兄弟,孙子或第三代表兄弟两次被删除,这些事件并不关心; event.preventDefault
只是意味着&#34;无论浏览器的默认行为是什么,都不要这样做。&#34;因此,表单提交上的event.preventDefault
会停止提交的表单,例如。
在此问题中描述的情况下,如果用户点击&#34;输入&#34;当按钮处于焦点时,按键是触发该按钮上的单击事件(即,是,鼠标事件),而不管该按钮在DOM中的位置。因此,在此处使用event.preventDefault
会阻止默认行为,这就是您想要的。
答案 1 :(得分:1)
首先,您需要在 inputKeyListener 函数中调用 missileLaunchButtonClickHandler 。
在您需要添加&#34; preventDefault&#34;到你的 missileLaunchButtonClickHandler 函数,因为当你按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
}
}