Mysterious mouse event closes jQuery UI dialog

This is obviously SSCCE .

Thus, we are instructed to write the front of the missile launch control system. We choose the Spartan layout considering that it is deadly: just enter a text input field and a button to enter the code:

enter image description here

For security reasons, when you click on the "OK" button, a dialog box appears asking you to confirm:

enter image description here

As a handy hatch, we'll add a key button listener Enterthat will also cause the OK button to be clicked (with $.trigger()

).

Unfortunately, the confirmation dialog is only displayed when the user clicks the "OK" button, but not when they click Enter. When we click Enter, no dialog will appear at all.

Worst of all, after adding some debug messages, it looks like the dialog is indeed displayed for a fraction of a millisecond and then the "Yeap" button is pressed for whatever reason. Therefore, when struck Enter, the rocket 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>
      

Run codeHide result


Update

In the above code, it is inputKeyListener

bound to keydown

in the document. Binding it closer to keydown

on the text input, as in:

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

      

& hellip; leads to precise behavior.

Update II

This answer suggests that it stopPropagation

is ineffective here because "the event bubble is not really playing here" and explains what preventDefault

to use to "[stop] the key event from accessing other elements of the page (that is, this button)". I am a little confused by these two statements taken together. I thought stopPropagation

this is exactly what is being used to stop the "key event from reaching other elements of the page". Moreover, there are two more confusion.

The first confusion is that the confirmation dialog is div

not the parent of the text input DOM div

, so it is unclear how the keyboard event on text input is div

intercepted by the sibling (non-parent) DOM element. I think this is actually the reason why it is stopPropagation

ineffective, but still it is not clear to me why (regardless of stopPropagation

) the event reaches the confirmation dialog button which is in marriage div

.

The second point of confusion is that if we register an event that we register in the function handler of the "Yeap" button, for example. eg:

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

      

& hellip; what we actually see in the console:

enter image description here

& hellip; so it's a mouse event , not a keyboard event that acknowledges the dialog. Considering that (in a scenario where one simple click Enter), only the mouse event we are creating is in inputKeyListener

:

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

      

& hellip; this means that it is this event that leads to the confirmation of the dialog, and not the keyboard event that we receive by pressingEnter

+3


source to share


2 answers


This is similar to the case where jQuery's UI is a little too useful for its own good: when opened, dialog

it puts the first button inside it in focus, just in time for the "enter" key event to trigger the button (which is the browser's default behavior when the user presses "enter" when the button has focus.)

Using preventDefault

in inputKeyListener

stops the key event from accessing other elements of the page (i.e. this button). stopPropagation

harmless, but will not have any useful effect either in inputKeyListener

or in missileLaunchButtonClickHandler

, because the event bubble does not really play here.

Here's a demo with no warning of Default or stopPropagation, and an empty button included to innocuously grab autofocus, just to confirm that this is 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>
      

Run codeHide result


on event.preventDefault vs event.stopPropagation



To expand on this, on "Update II": stopPropagation

Prevents event bubbles from spawning up to parent DOM nodes. Typically, for example, an event click bubbles upward from a node that is directly clicked on each parent node.

The reason stopPropagation

here is irrelevant because it is dialog

not the parent of the input element: the event bubble would not reach dialog

. So there is no reason to stop an event bubbling with help stopPropagation

, because it wouldn't trigger anything meaningful anyway.

Events stopped at event.preventDefault

, on the other hand, have nothing to do with the DOM structure - these events don't care if a parent, sibling, grandson, or third cousin is deleted twice; event.preventDefault

simply means "whatever the default browser behavior in this situation, don't do it." So event.preventDefault

on a form, submit stops the submitted form, eg.

In the case described in this question, the browser's default behavior, if the user presses the enter key when the button has focus, is to fire a click event on that button (that is, yes, a mouse event) regardless of whether where the button is in the DOM. So using it event.preventDefault

here prevents the default behavior you want.

+6


source


First you need to call missileLaunchButtonClickHandler inside your inputKeyListener function .

After you need to add "preventDefault" to your missileLaunchButtonClickHandler function , because the dialog is automatically closed when you press ENTER. preventDefault does not automatically close the dialog.

Modify the missileLaunchButtonClickHandler function :



function missileLaunchButtonClickHandler(e) {
   //e.stopPropagation();
   e.preventDefault();
   confirm();
 }

      

and change your inputKeyListener to this:

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
       }
     }

      

+1


source







All Articles