神秘的鼠标事件关闭了jQuery UI对话框 [英] Mysterious mouse event closes jQuery UI dialog

查看:59
本文介绍了神秘的鼠标事件关闭了jQuery UI对话框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这显然是



出于安全考虑,单击确定按钮后,我们将显示一个对话框,要求用户确认:





作为可用性完成触摸,我们为 Enter <添加一个关键监听器/ kbd>按钮也会导致单击确定按钮(使用 $。触发器())。



不幸的是,只有当用户点击确定按钮时才会显示确认对话框,但只有在点击 Enter 时才会显示。当我们点击 Enter 时,对话框根本不会出现。



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



小提琴



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

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

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

解决方案

这似乎是jQuery UI对自己的好处略微有用的情况:当一个对话框打开时,它将第一个按钮放在其中进入焦点,正好赶上输入键事件触发按钮(当按钮处于焦点时用户点击输入时,这是浏览器的默认行为。)



inputKeyListener 中使用 preventDefault 可以阻止键事件到达其他页面元素(即该按钮)。 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(); //直接调用confirm()不起作用confirm(); //也是! 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 called');},close:function(){console。 log('confirm :: close被称为');},resizable:false,height:auto,width:400,modal:true,buttons:{Hmmmm:function(){console.log('First button单击对话框内部。');},Yeap:function(){console.log('确认按钮被点击'); $(this).dialog(close); console.log('导弹发射代码['+ launchCode +']被确认了!');},也许还没有:function(ev){console.log('abort button被点击'); $(this).dialog(关闭); console.log('Armageddon被避免');}}}); dialog.dialog(开放); console.log('此时应该显示对话框');} $('#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 ='确认导弹发射'< / div> < span class =ui-icon ui-icon-alertstyle =float:left; margin:12px 12px 20px 0;>< / span>你确定要释放核世界末日吗?< / div>< / div>< div> < DIV> < div>输入导弹发射代码:< / 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>  



。 preventDefault vs event.stopPropagation



为了扩展这个,每个Update II: stopPropagation 防止事件冒泡到父DOM节点。通常,例如,单击事件从直接通过每个父节点单击的节点向上冒泡。



原因 stopPropagation 这里无关紧要因为对话框不是输入元素的父级:事件冒泡不会达到对话框 。所以没有理由用 stopPropagation 来阻止事件冒泡,因为它无论如何都不会触发任何有意义的事情。



相反, event.preventDefault 停止的事件与DOM结构无关 - 这些事件不关心父,兄弟,孙子或者第三堂兄两次搬走; event.preventDefault 只是意味着无论浏览器的默认行为是什么,都不要这样做。因此,表单提交上的 event.preventDefault 会停止提交的表单,例如。



在此问题中描述的情况下,如果用户在按钮处于焦点时点击enter键,则默认浏览器行为是触发点击事件(无论按钮位于DOM中的哪个位置,该按钮上的是,是,鼠标事件。因此,使用 event.preventDefault 可以防止出现这种默认行为。


This is obviously a SSCCE.

So we are tasked with writing the front-end of a missile launch control system. We opt for a Spartan layout given that this is deadly serious: just a text input box and a button to enter the code:

For safety purposes, upon clicking on the "OK" button we will display a dialog asking the user to confirm:

As a usability finishing touch we add a key listener for the Enter button that will also result in clicking the "OK" button (using $.trigger()).

Unfortunately, the confirmation dialog is only displayed when the user clicks on the "OK" button but not when hitting Enter. When we hit Enter the dialog does not appear at all.

Worst still, after adding some debugging messages it appears that the dialog is indeed displayed for a fraction of a millisecond and then for some reason the "Yeap" button is clicked. So when Enter is hit, missile launch is immediately confirmed!

Fiddle here.

Code below:

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>

Update

In the code above the inputKeyListener is bound to keydown on document. Binding it more narrowly to the keydown on the text input, as in:

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

… results in the exact same behavior.

Update II

This answer suggests that stopPropagation is ineffective here because "event bubbling isn't really in play here" and explains that preventDefault should be used to "[stop] the key event from reaching other page elements (i.e. that button)". I am a bit confused with those two statements taken together. I thought that stopPropagation is precisely what one uses to stop a "key event from reaching other page elements". Moreover there are two further points of confusion.

The first point of confusion is that the confirmation dialog div is not a parent DOM element of the text input div, so it is unclear how a keyboard event at the text input div is intercepted by a sibling (not parent) DOM element. I think this is in fact the reason that stopPropagation is ineffective but still it's not clear to me why (regardless of stopPropagation) the event reaches the confirmation dialog button which lies in a sibling div.

The second point of confusion is that if we log the event we capture in the "Yeap" button function handler, e.g. like this:

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

… what we in fact see in the console is:

… so it is a mouse event, not a keyboard event that confirms the dialog. Given that (in the scenario where one simple hits Enter) the only mouse event we are generating is in the inputKeyListener:

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

… this means that it is this event that results in the confirmation of the dialog, not the keyboard event we get by hitting Enter

解决方案

This appears to be a case of jQuery UI being slightly too helpful for its own good: when a dialog opens, it puts the first button inside it into focus, just in time for the "enter" key event to trigger the button (which is the browser's default behavior when the user hits "enter" while a button is in focus.)

Using preventDefault in your inputKeyListener stops the key event from reaching other page elements (i.e. that button). stopPropagation is harmless but will have no useful effect in either inputKeyListener or missileLaunchButtonClickHandler because event bubbling isn't really in play here.

Here's a demo with no preventDefault or stopPropagation, and a dummy button included to harmlessly catch the autofocus, just to confirm that this is what's happening:

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>

on event.preventDefault vs event.stopPropagation

To expand on this, per "Update II": stopPropagation prevents events bubbling up to parent DOM nodes. Normally, for example, click events bubble upwards from the node directly clicked on through every parent node.

The reason stopPropagation is irrelevant here is because dialog is not a parent of the input element: event bubbling wouldn't have reached dialog. So there's no reason to stop the event bubbling with stopPropagation, because it wouldn't have triggered anything meaningful anyway.

The events stopped by event.preventDefault, by contrast, have nothing to do with the DOM structure -- these events don't care if parent, sibling, grandchild, or third cousin twice removed; event.preventDefault simply means "whatever the default behavior of the browser would have been in this situation, don't do that." So event.preventDefault on a form submit stops the form being submitted, for example.

In the case described in this question, the default browser behavior if the user hits the "enter" key while a button is in focus is to trigger a click event (which is, yes, a mouse event) on that button, regardless of where the button is in the DOM. So using event.preventDefault here prevents that default behavior, which is what you want.

这篇关于神秘的鼠标事件关闭了jQuery UI对话框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆