簡體   English   中英

神秘的鼠標事件關閉了jQuery UI對話框

[英]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);

...導致完全相同的行為。

更新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”鍵事件觸發按鈕(這是當按鈕處於焦點時用戶點擊“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); 
 <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> 

在event.preventDefault vs event.stopPropagation上

為了擴展這一點,根據“Update II”: stopPropagation可防止事件冒泡到父DOM節點。 通常,例如,單擊事件從直接通過每個父節點單擊的節點向上冒泡。

stopPropagation與此無關的原因是因為dialog不是input元素的父元素:事件冒泡不會到達dialog 所以沒有理由用stopPropagation來阻止事件冒泡,因為無論如何它都不會觸發任何有意義的事情。

相反, event.preventDefault停止的事件與DOM結構無關 - 這些事件不關心父母,兄弟,孫子或第三代表兄弟兩次被刪除; event.preventDefault只是意味着“無論瀏覽器的默認行為是什么,都不要這樣做。” 因此,表單提交上的event.preventDefault停止正在提交的表單。

在此問題中描述的情況下,如果用戶在按鈕處於焦點時點擊“輸入”鍵,則默認瀏覽器行為是觸發該按鈕上的單擊事件(即,是,鼠標事件),無論在何處按鈕位於DOM中。 所以在這里使用event.preventDefault可以防止這種默認行為,這就是你想要的。

首先,你需要調用missileLaunchButtonClickHandlerinputKeyListener函數中。

在您需要向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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM