jakee
jakee

Reputation: 18566

Destroying a dialog when it loses focus

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.

UPDATE:

I found an agreeable answer to my question with the help of underscore.js:

I broke my problem down to parts:

  1. A focusout happens on the first click
  2. If the click was OUTSIDE the dialog, there were no further events
  3. If the click was INSIDE the dialog, a focusin event followed

So 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

Answers (5)

mhodges
mhodges

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

Decebal
Decebal

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

Scott Arrington
Scott Arrington

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

Greg Motyl
Greg Motyl

Reputation: 2545

blur should work for you:

$('.dialog').trigger('click');
$('.dialog').blur(function() {
   this.dialog('destroy');
}

Upvotes: 0

Jon
Jon

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

Related Questions