Single ESC closes ALL modal dialogs in jQuery UI. Workarounds? - javascript

Actually, there was (is still) a bug in jQuery: http://bugs.jqueryui.com/ticket/4511.
The reason for this behavior (from the bug description comments): "The dialog itself binds keydown event to itself for closing the dialog on ESC; in addition, the dialog overlay binds a keydown event to the document, without filtering to close only the active dialog."
I cannot come up with an idea of an acceptable workaround. Is there anyone who has had to deal with it yet?

Very simple - upon creating a modal dialog, run this:
$([document, window]).unbind('.dialog-overlay');
If you create more then one modal dialog, hitting ESC will close the top one only.
Then once you focus on the bottom dialog, hit ESC and it will close it as well.
Hope this helped!
P.S. jQuery UI developers should add an option when you want all dialogs close at once upon hitting ESC key or only the focused ones.

Easiest thing is I have added event.stopPropagation(); in close function before return self; in jquery.ui.dialog.js file. And I am done with problem of closing dialog boxes one by one on escape keydown. Let me know if anyone find any better solution.
EDITED:
this need to add because while clicking on close button event object is undefined.
if(typeof event != 'undefined'){
event.stopPropagation(); }

The root of the problem is that jQuery UI keydown event propagates through all dialogs. A fix in the original jQueryUI Dialog code would be to add event.stopPropagation() when topmost dialog was successfully closed and check event.isPropagationStopped() at the beginning of the same keydown event.
As a workaround I did, thanks for Jazzer, the following.
Set dialog option closeOnEscape to false
When dialog gets created, append:
//newDialog is dialog's content, e.g. var newDialog = $('my dialog content>');
newDialog.keydown(function(event) {
if (mydialogs.hasOpenDialogs() &&
event.keyCode &&
event.keyCode === $.ui.keyCode.ESCAPE) {
$(newDialog).dialog("close");
event.preventDefault();
event.stopPropagation();
}
});
when document is loaded do:
$(function(){
//allow for ESC to close only top dialog
$(document).bind('keydown', function(event) {
if (event.isPropagationStopped()) return true;
if (mydialogs.hasOpenDialogs() &&
event.keyCode &&
event.keyCode === $.ui.keyCode.ESCAPE) {
mydialogs.closeTopDialog();
event.preventDefault();
event.stopPropagation();
}
});
});
Event in (2) happens when user hits ESC while typing text in input box inside of the dialog.
Event in (3) happens when user hits ESC somewhere else.
mydialogs here is a wrapper around stack (array) of modal dialogs, where every new dialog adds itself via .push() and in .close() removes itself with .pop().

Related

Disable Esc key to close panel on jQuery Mobile

According to jQuery Mobile's documentation:
"Clicking the link that opened the panel, swiping left or right, or tapping the Esc key will close the panel. (...) By default, panels can also be closed by clicking outside the panel onto the page contents."
http://api.jquerymobile.com/panel/
The same documentation shows how to turn off "swipe to close" and "close by clicking outside".
But how to disable closing by the Esc key?
You could do something like this:
$("body").on("keyup", function(e){
if (e.which === 27){
return false;
}
});
This will disable the escape key from closing a panel when one is open. This might interfere with other functions, and you probably could specify what element you want the event attached to. You can read more on return false here.
I made a very simple and lazy example of it working.

Space and enter "click" on the input that's focused. How do I disable this behavior?

In chrome and firefox (and maybe others), if you've got an input focused, pressing "space" and "enter" clicks them for you. I'm making an HTML 5 game and I want to rewrite how space and enter reacts on focus and the default behavior is getting in my way. Is there a way to turn this default behavior off in most browsers?
Here's a jsfiddle demonstrating the problem.
<button>Button<button>
$("button").on("click", function(event) { alert("Clicked"); });
If you click on the button, it displays the alert which is good. But if you press "space" or "enter" after you click it, it also alerts. I want to prevent this behavior so that I can write my own without them interfering.
You can fix this by using event.detail. That will return the amount of times the button has been clicked. If you press enter, this returns 0, since you clicked it 0 times, and if you click it via your mouse, it returns the amount of times you clicked the button.
To access event.detail, you need to access the original event object. This can be done via event.originalEvent in the jQuery event object. So, if you just put an if statement in your script:
if (event.originalEvent.detail != 0) {
//your click event code
}
then it'll only run if you actually click the button via your mouse.
This will be much more accurate than checking if the button has :focus, since the button automatically gets focused when you click it, so doing that would disable the button after a single click.
Check if a button is active:
$("button").on("click", function(event) { alert("Clicked"); });
$(document).on('keydown', function(e){
if($(document.activeElement).is('button') &&
(e.keyCode === 13 || e.keyCode === 32))
e.preventDefault();
});
You could also use jQuery's :focus selector, which should return the same element, $(':focus').is('button').
http://jsfiddle.net/zmH5V/4/
other option, is to blur the object right after clicking it:
<button id="mybutton" onclick="myFunction();this.blur();">button</button>
I find that solution easier to use, because it requires less code-lines, and gets the same results:
while the button is blured, it has no contact with the keyboards events, and that solves the problem.

Finding out what event is firing on an element?

I am using jQuery to manage a keypress on my document and open a new window:
$(document).keypress(function(event){
var keycode = (event.keyCode ? event.keyCode : event.which);
if(keycode == '13') {
// Open a new window
}
});
The problem is I have a jQuery accordion that I want to stay CLOSED until a user explicitly clicks on it. I managed to get the accordion to not open on the 'enter' keypress, but if another window opens in a new tab and becomes active, the accordion opens.
Problem is I have no idea to find out what even is getting fired on the accordion that allows it to open.
Is there a way to nuke all events on an element and then just add back the one that you want (in my case the mouse down or click)? Or to report which events are being handled by that element so that I could just try to unbind that one?
Have you tried reading through the "active" portion in
http://api.jqueryui.com/accordion/#option-active ?
i.e Setting active to false will collapse all panels
I've found this scriptlet to be really helpful when trying to find out which events are attached to which dom elements. I might help you work out what's going on - Visual Event

Override default Tab Behavior to keep focus on browser form

I'm building my first application where I have to have compliance with keyboard navigation for accessibility reasons.
My problem has to do jquery-ui modal dialog boxes. If the user presses tab on the last control of the dialog (cancel button for this app), focus goes outside of the dialog box. Or presses shift-tab on the first control in the dialog box.
When the user does this, it isn't always possible to tab back into dialog box. IE8 and FF8 behave somewhat differently in this respect. I've tried to capture the tab key with the following event handler -
lastButton.keydown(function (e) {
if (e.which === TAB_KEY_CODE) {
e.stopPropagation();
$(this).focus();
}
});
But this doesn't work as it appears the browser processes the key press after jquery is done.
Two questions -
For Accessibility compliance, do I even have to worry about this? Although, for usability reasons, I think that I should.
Is there a way to make this work?
My problem has to do jquery-ui modal dialog boxes. If the user presses tab on the last control of the dialog (cancel button for this app), focus goes outside of the dialog box. Or presses shift-tab on the first control in the dialog box.
... and then tabbing occurs below the modal box, under a grey semi-transparent layer with scrollbar jumping from bottom to top after a few keypresses? Yes, this is a concern for sighted users who use the keyboard to browse and won't know how to go back to the modal box without pressing Tab a hundred times. Blind people won't even know the modal box is still displayed (they still can see/hear the entire DOM with their screen reader!) and that the page/script is waiting for a submit or cancel decision so it's also a concern for them.
An example done right is shown at http://hanshillen.github.com/jqtest/#goto_dialog (click on Dialog tab, direct link with anchor doesn't work :/ ). It'll tab forever inside the modal box till you click on Close or OK and will put you back on the focused element that triggered the modal box (I think it should focus the next focusable element after leaving the modal box but nevermind, this isn't the biggest accessibility problem here).
This serie of scripts is based on jQueryUI and are highly improved for keyboard and ARIA support and any accessibility problem that could exist in the original scripts. Highly recommended! (I tried to mix jQuery UI original scripts and these ones but didn't manage to get anything working, though you don't need to do so: these scripts work fine by themselves)
Maybe you should prevent the default action with preventDefault() instead of stopping the propagation and use keypress instead of keydown.
In this way there should be no need to regain focus.
Stopping the propagation doesn't work because it just prevent the event from bubbling up. You could think about using stopImmediatePropagation() but i think that changing input on the pression of the tab can't be stopped that way and preventDefault() is more correct.
lastButton.keypress(function (e) {
if (e.which === TAB_KEY_CODE) {
e.preventDefault();
}
});
fiddle here: http://jsfiddle.net/jfRzM/
Im a little late to the party, but I found I had to call preventDefault in the other keyboard events as well.
ex) I was setting the focus in the keyup event. But the browser was still doing its thing in either keydown or keypress. So I had something like this (I used JQuery/Typescript, but the idea should translate to about anything):
elem.keyup(this.onDialogKeyPress);
elem.keydown(this.onDialogPressPreventDefault);
elem.keypress(this.onDialogPressPreventDefault);
...
private onDialogPressPreventDefault = (e: KeyboardEvent) => {
const keys = [9, 27];
if (keys.includes(e.which)) {
e.preventDefault();
return false;
}
}
private onDialogKeyPress = (e: KeyboardEvent) => {
// Tab
if (e.which == 9) {
e.preventDefault();
// Do tab stuff
return false;
}
// Esc
else if (e.which == 27) {
e.preventDefault();
// Do Esc stuff
return false;
}
}

How do i find the new focus item with jquery?

I have a pop up dialog that lets you write text and does stuff when you click a button. My code is below
This function works, i find the new object by looking at e.originalEvent.explicitOriginalTarget. However now i notice if i press tab this function will be called but e.originalEvent.explicitOriginalTarget will give me the same current object instead of the new object. So my dialog doesnt close if a user presses tab to leave. How do i find the correct new dom item?
$('#Area').focusout(function (e) {
if ($(e.originalEvent.explicitOriginalTarget).closest('#Area').size() == 0)
$('#Area').hide();
});
event.relatedTarget worked for me. It will give the other DOM element, within the event, if there is one.
An example would be, if you had 2 buttons controlling the same function, and didn't want their code to be executed if they were clicked consecutively. You could attach a focusout event handler and check for an ID, or a class name.
$(".buttons").on("focusout", function (event) {
if($(event.relatedTarget).prop("class").indexOf("buttons") === -1) {
//code to execute
}
});
Perhaps a better example would be the issue I had.
I created a custom drop down list, that has a button beside it. The drop down list can be opened and closed by either clicking on the list, or the button. It can also be closed be losing focus to either object.
This becomes a problem in the following scenario.
1) user opens drop down list by clicking the list object.
2) user closes drop down list by clicking the button.
What happens is the list opens, but when the user goes to close the list, the list loses focus, which closes it, but since they are clicking on the button, it opens back up. The focusout causes the two objects to cancel each other out, in this type of scenario.
By writing the focusout event, I can now set it to only trigger when the relatedTarget doesn't have the same class as the target that called the event.
$(".listControl").on("focusout", function (event) {
if($(event.relatedTarget).prop("class").indexOf("listControl") === -1) {
//Close the drop down list
}
});
http://api.jquery.com/event.relatedTarget/
Check out this question/answer How to select an element that has focus on it with jQuery
I think the reason why you don't get anything with $("*:focus"); in Firebug console is when you click the console, the element loses focus.
And if you want to tackle it with events, the opposite of focus() is blur().
Edit
Maybe you can even try a different approach. If your only concern is watching for tab key, you can use .keypress() event and watch for tab keycode which is 9.

Categories