Reputation: 18566
I have a program where I have jQuery UI dialogs. I wish that those dialogs would be closed when they lose focus e.g. the user clicks anywhere outside the dialogs in question.
On the surface this seems like a simple problem, but the blur
event didn't work at all. After that I moved on to focusin
and focusout
, but stumbled upon a strange problem:
When I have a dialog and have attached focusin
and focusout
to the element containing the dialog (the parent of the element I called dialog
on). The first click after the dialog's appearing ALWAYS produces a focusout
event regardless of where the click lands.
So if I would like to destroy the dialog on focusout
, the dialog gets destroyed when it shouldn't be. Here is jsFiddle that demonstrates my problem.
I know I could use some trick to ignore the first focusout
, but I'd like to know if there is any way to get this working the right way, which is that the only event is produced when the user clicks outside the dialog for the first time.
I found an agreeable answer to my question with the help of underscore.js
:
I broke my problem down to parts:
focusout
happens on the first clickfocusin
event followedSo the undesired behavior took place when there was a sequence of focusout
->focusin
. I used underscore's defer
function and a boolean modifier to tackle this problem
onFocusout: function(event) {
console.log('focus out');
this.hasFocus = false;
var self = this;
_.defer(function() {
if (!self.hasFocus) alert('DESTROY DIALOG');
});
},
onFocusin: function() {
console.log('focus in');
this.hasFocus = true;
},
defer
makes the function wait until the call stack has cleared (in the bad behaving case, there is a focusin
event waiting in the stack) and then executes it. So if onFocusin
is fired right after onFocusout
the hasFocus
variable is changed to true
and the deferred function will not destroy the dialog!
Thank you for your answers!
Upvotes: 1
Views: 6252
Reputation: 11116
Using jQuery, this is fairly straight forward. The way that jQuery structures its dialogs is that whenever a dialog becomes active, it places a div that covers all of your other page content and has a class of .ui-widget-overlay and it has a z-index of x (in my case, 100). Whenever a dialog becomes active, it will give that dialog, and that dialog only a z-index of x + 1.
$(document).on('click', '.ui-widget-overlay', function () {
var overlay = $(this);
var dialogToClose = null;
$.each($(".dialog"), function () {
if ($(this).zIndex() == (overlay.zIndex() + 1)){
dialogToClose = $(this);
}
});
dialogToClose.dialog("close");
});
Upvotes: 0
Reputation: 1419
Here is my workaround, it works in a page with multiple dialogs too, also updated solution of jquery 'live' method:
$(document).on('click', '.ui-widget-overlay', function() {
var dialogAria = $(this).next().attr('aria-describedby');
$('#'+dialogAria).dialog("close");
});
i've been inspired by Scott's answer
Upvotes: 0
Reputation: 12503
Just FYI, for a jQuery modal dialog, it places a div with the class ui-widget-overlay
on top of your content. Therefore, I just did something like this:
$(".ui-widget-overlay").live("click", function() {
$("#myDialogDiv").dialog("close");
});
It works like a charm :)
Upvotes: 1
Reputation: 2545
blur should work for you:
$('.dialog').trigger('click');
$('.dialog').blur(function() {
this.dialog('destroy');
}
Upvotes: 0
Reputation: 437396
An option that would probably work better is to listen for clicks anywhere on the page and close the dialog if one occurs outside the dialog area. Something like:
$(document).on("click", function(e) {
var clickedOnDialog = $(e.srcElement)
.closest(".ui-widget.ui-dialog") // these classes are fixed
.children(".ui-dialog-content") // this as well
.is(".dialog"); // this is your own class
if (!clickedOnDialog) {
$('.dialog').dialog('destroy');
}
});
Demo.
Upvotes: 6