[英]Mysterious mouse event closes 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);
<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);
...導致完全相同的行為。
這個答案表明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”鍵事件觸發按鈕(這是當按鈕處於焦點時用戶點擊“enter”時的瀏覽器默認行為。)
在inputKeyListener
使用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);
<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>
為了擴展這一點,根據“Update II”: stopPropagation
可防止事件冒泡到父DOM節點。 通常,例如,單擊事件從直接通過每個父節點單擊的節點向上冒泡。
stopPropagation
與此無關的原因是因為dialog
不是input元素的父元素:事件冒泡不會到達dialog
。 所以沒有理由用stopPropagation
來阻止事件冒泡,因為無論如何它都不會觸發任何有意義的事情。
相反, event.preventDefault
停止的事件與DOM結構無關 - 這些事件不關心父母,兄弟,孫子或第三代表兄弟兩次被刪除; event.preventDefault
只是意味着“無論瀏覽器的默認行為是什么,都不要這樣做。” 因此,表單提交上的event.preventDefault
停止正在提交的表單。
在此問題中描述的情況下,如果用戶在按鈕處於焦點時點擊“輸入”鍵,則默認瀏覽器行為是觸發該按鈕上的單擊事件(即,是,鼠標事件),無論在何處按鈕位於DOM中。 所以在這里使用event.preventDefault
可以防止這種默認行為,這就是你想要的。
首先,你需要調用missileLaunchButtonClickHandler您inputKeyListener函數中。
在您需要向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
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.