Question

I'm writing my own ESC handler because I need to do other actions when ESC is pressed, specifically I need to manage where focus goes for keyboard-only users. I have it working for all menus and some dialogs (both of which are using jQueryUI) but I'm having problems with dialogs that open on top of other dialogs (confirmation dialogs).

I'm using a Backbone View and adding my keydown handler on dialogcreate. this.$el.on('dialogcreate', this.bindKeydownEvent);

My handler:

bindKeydownEvent: function(ev, ui) {
  var self = this;
  this.$el.dialog().on('keydown', function(evt) {
    if(evt.keyCode === $.ui.keyCode.ESCAPE) {
      self.$el.dialog("close");
      if(self.options.closeFocusEl) {
        $(self.options.closeFocusEl).focus();
      }
      evt.stopPropagation();
    }
  });
}

I've checked and this.$el.dialog() is the correct dialog when the second dialog calls this.bindKeydownEvent but for some reason the keydown handler is not being triggered no matter what I press in the dialog (Tab, Space, Enter, random letters, etc).

Any idea what I'm doing wrong or have a better way I could bind the keydown event?

EDIT:

I just noticed that this is also happening in some first-level dialogs. It looks like the only difference is the way we get the template and therefore create the interior of the dialog. In our Alert and Confirmation dialog classes, we define the template as an attribute on the object like this: template: _.template("<div><%= o.content %></div>"). In other views (in which the keydown binding works) we build the child elements and add them to the DOM of the dialog, set the template in the initialize function

  this.options.template = 'navigation/CreateNewDialog.template';

or set it when we call the dialog

var closeConv = new views.CloseConversationDialogView({
  confirm: this.closeConversationConfirmed,
  content: i18n.t("closeConversationInput"),
  template: "conversation/CloseConversationDialog.template"
});
closeConv.render();

Is there a reason that creating the template inline as an attribute on the view would not bind keydown correctly?

Was it helpful?

Solution 2

The only thing my original attempt was missing was "widget". The widget method, according to api.jqueryui.com,

Returns a jQuery object containing the generated wrapper.

I don't see any documentation on what exactly $('.selector').dialog() returns but apparently it is not the same as $('.selector').dialog("widget"). I also changed on('keydown'... to just use the jQuery keydown instead.

bindKeydownEvent: function(ev, ui) {
  var self = this;
  this.$el.dialog("widget").keydown(function(evt) {
    if(evt.keyCode === $.ui.keyCode.ESCAPE) {
      self.$el.dialog("close");
      if(self.options.closeFocusEl) {
        $(self.options.closeFocusEl).focus();
      }
      evt.stopPropagation();
    }
  });
}

OTHER TIPS

To understand why your event handler isn't being triggered you need first understand how event delegation works.

The key to event delegation in that events bubble up the DOM. So when you bind your event using this.$el.dialog().on('keydown',..., what you basically doing is listening to any keydown event that is triggered on your $el or it's descendants. In this case being that your second dialog isn't a descendant of your $el it's events won't bubble up to it and therefore don't trigger your handler.

To work around this you can either bind directly to your second dialog, or instead bind to a exisitng higher level element like the document. For example

$(document).on('keydown', '.myDialog', function() {...
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top