How to intercept jQuery Dialog ESC key event?
Asked Answered
O

5

16

I have a modal jQuery dialog and another element that takes the ESC key event behind the dialog. When the jQuery Dialog is up, I don't want this ESC key event to propagate. What happens now is that when I click on ESC, it will close the dialog and trigger the ESC event handler on the background element.

How do I eat the ESC key event when a jQuery dialog is dismissed?

Outwork answered 5/5, 2012 at 23:12 Comment(0)
R
24

Internally jQuery UI's dialog's closeOnEscape option is implemented by attaching a keydown listener to the document itself. Therefore, the dialog is closed once the keydown event has bubbled all the way to the top level.

So if you want to keep using the escape key to close the dialog, and you want to keep the escape key from propagating to parent nodes, you'll need to implement the closeOnEscape functionality yourself as well as making use of the stopPropagation method on the event object (see MDN article on event.stopPropagation).

(function() {
  var dialog = $('whatever-selector-you-need')
    .dialog()
    .on('keydown', function(evt) {
        if (evt.keyCode === $.ui.keyCode.ESCAPE) {
            dialog.dialog('close');
        }                
        evt.stopPropagation();
    });
}());

What this does is listen for all keydown events that occur within the dialog. If the key pressed was the escape key you close the dialog as normal, and no matter what the evt.stopPropagation call keeps the keydown from bubbling up to parent nodes.

I have a live example showing this here - http://jsfiddle.net/ud9KL/2/.

Retain answered 6/5, 2012 at 12:38 Comment(4)
this doesnt work, I added a simple alert in your jsfiddle inside the keydown function and it never gets called :(Parkerparkhurst
Unfortunately closeOnEscape doesn't work for me. But this binding did the trick.Carmancarmarthen
This wont work if the dialog doesnt have an 'input' element on it, or if the focus leaves that element and is on the actual dialog box. You need to bind the keydown to the element above with parent, so you need to change the ".on('keydown'" bit to ".parent().on('keydown'"Sectarianize
^^ what @RobDonovan commented is true.. if there is no input or there's no input in focus, it won't catch the key. But if you put the keydown handler on the dialog parent then it's all good. Also, what I found is, you have to explicitly set the dialog closeOnEscape: false otherwise the dialog will still install its own handler and cause problems.Mabel
P
20

You need closeOnEscape...

Example code:

$(function() {
$("#popup").dialog({
height: 200,
width: 400,
resizable: false,
autoOpen: true,
modal: true,
closeOnEscape: false
});
});

See it live: http://jsfiddle.net/vutdV/

Plural answered 5/5, 2012 at 23:27 Comment(2)
He didn't say that he wants the dialog to not close on escape, he said he doesn't want the other element that listens for the escape to ignore it if the dialog is open. Your way would remove the functionality of closing the dialog by pressing escape.Morganmorgana
Uou do have a point there. I edited the style tags out, so there still is a "close" link available. jsfiddle.net/vutdV/1 Still not the final solution indeed.Plural
B
5

You can use the following

    $(document).keyup(function (e) {
        if (e.keyCode == 27) {

            $('#YourDialogID').dialog('close')  
        }
    });
Branch answered 18/11, 2013 at 11:30 Comment(0)
M
0

You would need to modify the code for your element behind the dialog to see if the dialog was open AND the escape key was pressed and to ignore that situation.

Morganmorgana answered 5/5, 2012 at 23:14 Comment(3)
Thanks for the answer. But the thing is, when I hit ESC, the event goes to the dialog first and dismiss the dialog. Only then it goes to my background element, which at the point will find out that the dialog is not there and take the ESC event.Outwork
What about removing the listener from the background element when the dialog is opened, and then reattaching it once the dialog has closed?Morganmorgana
I guess that will work, but wouldn't that be a little hacky? Will there be a less hacky way to do it?Outwork
L
0

I wanted to do a similar but opposite thing - prevent the ui dialog closing if we hit ESC in a modal window opened from inside the dialog while the dialog is still open.

Thus if the modal window is open on top of the ui dialog then ESC must only close the modal window and leave the ui dialog open. I don't want to disable closeOnEscape for the dialog, just suppress it if another modal is open over it.

I had no joy with $(document).on("keydown", ...) or $dialog.on("keydown", ...) in jquery-ui-1.12.1. I call the widget methods _off and _on to remove and replace the original keydown event with my own intercept event where I can either do nothing or run the original handler code.

There may be a more elegant way to call the original keydown event without duplicating the code, but I have not figured that out yet.

I call PatchCloseOnEscape($dialog) just after creating it with $dialog.dialog(...).

/**
 * Tedium required to prevent the jQuery-UI dialog closing when we hit the ESC key to close
 * a modal window opened from the dialog while the dialog is still open.
 */
private static PatchCloseOnEscape($dialog: JQuery): void
{
    // Get dialog widget
    let dialog = $dialog.data("ui-dialog");

    // Remove original keydown event
    dialog._off(dialog.uiDialog, "keydown");

    // Add our new keydown event
    dialog._on(dialog.uiDialog, {
        keydown: function (event)
        {
            // Only close on escape if a modal window is not showing.
            if (<check if modal window is open> == true)
            {
                return;
            }

            ////////////////////////////////////////////////////////////////////////////////
            // Copied from original keydown event in jquery-ui-1.12.1.js line 12278 - 12305

            if (this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
                event.keyCode === (<any>$).ui.keyCode.ESCAPE)
            {
                event.preventDefault();

                this.close(event);

                return;
            }

            // Prevent tabbing out of dialogs
            if (event.keyCode !== (<any>$).ui.keyCode.TAB || event.isDefaultPrevented())
            {
                return;
            }
            var tabbables = this.uiDialog.find(":tabbable"),
                first = tabbables.filter(":first"),
                last = tabbables.filter(":last");

            if ((event.target === last[0] || event.target === this.uiDialog[0]) &&
                !event.shiftKey)
            {
                this._delay(function ()
                {
                    first.trigger("focus");
                });
                event.preventDefault();
            } else if ((event.target === first[0] ||
                event.target === this.uiDialog[0]) && event.shiftKey)
            {
                this._delay(function ()
                {
                    last.trigger("focus");
                });
                event.preventDefault();
            }

            ////////////////////////////////////////////////////////////////////////////////
        }
    });
}
Leakage answered 12/8, 2020 at 13:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.