Mar*_*tus 3 javascript jquery jquery-ui jquery-ui-dialog
这显然是一个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);Run Code Online (Sandbox Code Playgroud)
<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>Run Code Online (Sandbox Code Playgroud)
在上面的代码inputKeyListener中绑定到keydown文档.将它更窄地绑定到keydown文本输入上,如:
$('#missile-launch-code-input').on('keydown', inputKeyListener);
Run Code Online (Sandbox Code Playgroud)
...导致完全相同的行为.
这个答案表明stopPropagation这里效果不佳,因为" 事件冒泡在这里并没有真正发挥作用 ",并解释说preventDefault应该用来" [停止]关键事件到达其他页面元素(即那个按钮) ".我对这两个一起的陈述有点困惑.我认为stopPropagation是正是什么人用来"停止到达其他网页元素的关键事件 ".此外还有两个混乱点.
第一个困惑点是确认对话框div不是文本输入的父DOM元素div,因此不清楚文本输入中的键盘事件如何div被兄弟(而不是父)DOM元素拦截.我认为这实际上stopPropagation是无效的原因,但我仍然不清楚为什么(不管stopPropagation)事件到达兄弟姐妹的确认对话框按钮div.
第二个困惑点是,如果我们记录我们在"Yeap"按钮函数处理程序中捕获的事件,例如:
buttons: {
"Yeap": function(ev) {
console.log(ev);
Run Code Online (Sandbox Code Playgroud)
...我们实际上在控制台中看到的是:
...所以这是一个鼠标事件,而不是确认对话框的键盘事件.鉴于(在一次简单命中的情况下Enter)我们生成的唯一鼠标事件是inputKeyListener:
$('#missile-launch-button').click();
Run Code Online (Sandbox Code Playgroud)
...这意味着正是这个事件导致了对话框的确认,而不是我们通过点击获得的键盘事件 Enter
这似乎是jQuery UI对其自身的好处略微有用的情况:当dialog打开时,它将其中的第一个按钮置于焦点,正好赶上"enter"键事件触发按钮(这是当按钮处于焦点时用户点击"enter"时的浏览器默认行为.)
使用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);Run Code Online (Sandbox Code Playgroud)
<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>Run Code Online (Sandbox Code Playgroud)
为了扩展这一点,根据"Update II": stopPropagation防止事件冒泡到父DOM节点.通常,例如,单击事件从直接通过每个父节点单击的节点向上冒泡.
原因stopPropagation与此无关,因为dialog它不是input元素的父元素:事件冒泡不会达到dialog.因此,没有理由阻止事件冒泡stopPropagation,因为无论如何它都不会触发任何有意义的事情.
event.preventDefault相反,停止的事件与DOM结构无关 - 这些事件并不关心父母,兄弟,孙子或第三代表兄弟两次被删除; event.preventDefault只是意味着"无论浏览器的默认行为是什么,都不要这样做." 因此event.preventDefault,在表单提交上会停止提交的表单,例如.
在此问题中描述的情况下,如果用户在按钮处于焦点时点击"输入"键,则默认浏览器行为是触发该按钮上的单击事件(即,是,鼠标事件),无论在何处按钮位于DOM中.所以event.preventDefault在这里使用可以防止这种默认行为,这就是你想要的.