enlace de eventos con .on () para los diálogos de JQueryUI dentro de las pestañas de JQueryUI

I have a page with links of class 'dialog' that generate in JQueryUI diálogo when clicked. Those dialogs are created from other elements present on the page, and can contain links of class 'add_tab', that should create a new JQueryUI [tab] (http://jqueryui.com/demos/dialog/) when clicked. Those tabs load their content via Ajax and consist identical structures. This means that an 'add_tab' link in a dialog creates a new tab, which contains 'dialog' links that generate dialogs containing further 'add_tab' links, and so on.

This is the basic HTML structure:

<div id="tabs">
  <ul>
    <li><a href="#tabs-1">tab 1</a></li>
  </ul>
  <div id="tabs-1">
    <p>This tab contains a <a href="#popup1" class="dialog" target="_new">popup</a> and a direct link to a <a href="tabs2.htm" class="add_tab">new tab</a>.</p>
    <div id="popup1" style="display:nonee;">This popup contains a link to a <a href="tabs2.htm" class="add_tab" target="_new">new tab</a>.</div>
  </div>

Using JQuery 1.7's .on () método, I have troubles with properly registering the click handler for 'add_tab' links that appear in dialogs on added tabs. I manage to register the click handlers for the 'dialog' links in newly generated tabs (so that they generate a dialog), but fail to register click handlers for 'add_tab' links that appear dentro those dialogs. I've put a simplified test version online at http://www.kantl.be/ctb/temp/jquerytest/tabs1.htm. Take, for example following scenario:

  1. on http://www.kantl.be/ctb/temp/jquerytest/tabs1.htm , click 'popup': this will generate a JQueryUI dialog
  2. in the dialog, click 'new tab': this will generate a new JQueryUI tab
  3. in the the newly added tab labeled 'tabs2.htm', click 'popup': this will generate a JQueryUI dialog
  4. in the dialog, click 'new tab': this will NOT generate a new JQueryUI tab, but instead open the target in a new window

    ==> this illustrates how this event handler is apparently NO registered correctly for 'add_tab' links that occur inside dialogs that are generated in newly added tabs

  5. in the tab labeled 'tabs2.htm', click 'new tab': this will generate a new JQueryUI tab

    ==> this illustrates how this event handler is registered correctly for 'add_tab' links that occur directly inside newly added tabs

Este es mi código javascript:

// these event registrations register clicks on $('a.dialog') and $('a.add_tab') to open new JQueryUI dialogs / tabs
// note: this event registration works for all such links on the original page
$('a.dialog').on('click', function(){
  $($(this).attr('href')).dialog(); 
  return false; 
});
$('a.add_tab').on('click', function(){
  $tabs.tabs( "add", $(this).attr('href'), 'added tab'); 
  $('.ui-dialog-content').each(function(){$(this).dialog('close')}) 
  return false; 
}); 
// tabs: upon creation, register clicks on nested $('a.dialog') and $('a.add_tab') to open new JQueryUI dialogs / tabs 
var $tabs = $( "#tabs" ).tabs({    
  add: function(event, ui) { 
    $tabs.tabs('select', '#' + ui.panel.id); 
    $tabs.tabs($tabs.tabs('option', 'selected')) 
      .on('click', 'a.dialog', function(){ 
        $($(this).attr('href')).dialog(); 
        return false; 
      })
      // this registration doesn't seem to work for <a class="add_tab"> links occurring inside generated JQueryUI dialogs inside added JQueryUI tabs          
      .on('click', 'a.add_tab', function(){ 
        $tabs.tabs( "add", $(this).attr('href'), 'added tab'); 
        return false; 
      }); 
  }
});

estoy hace casi there! Could anyone help me out with the last event handler in the code above? Any help is much appreciated!

preguntado el 01 de febrero de 12 a las 22:02

2 Respuestas

The idea with event delegation is to bind the event on a con el futuro bebé that is fixed on the page (not created dynamically). Using the selector parameter of the .on() method allows telling for which elements the event handler should be fired for.

In your code you are using event binding in two ways, event if you use .on():

  • first you do direct binding on the existing elements a.dialog and a.add_tab - this will only work for the links on tab1 as they are the only the ones existing at the time the code is executed, no event delegation here.

  • when adding a tab, you are doing event delegation on the tab container $tabs:

    • for the links to open a dialog with $tabs.on('click', 'a.dialog', function(){ ... }) - this works as expected because the link a.dialog are indeed within the tab container.

    • for the links in the dialog with $tabs.on('click', 'a.add_tab', function(){ ... }) - this won't work because when the dialog is created, the plugin moves the <div id="popup2"> at the end of the body (before </body>). So when you click the a.add_tab inside the dialog, it is not a descendant of the tab container anymore and event delegation does not happen.

Esto es lo que haría:

  1. to avoid repeating the same code, declare your event handlers as variables

  2. use event delegation on the tab container for links a.dialog and a.add_tab

  3. when creating a new dialog, use event delegation on the dialog for the a.add_tab links it will contain

Aquí está el código:

var addTabClickHandler = function(e) {
    $tabs.tabs("add", $(this).attr('href'), 'added tab');
    $('.ui-dialog-content').each(function() {
        $(this).dialog('close')
    })
    return false;
};

var dialogOpenClickHandler = function(e) {
    $($(this).attr('href'))
        .dialog()
        .on('click', 'a.add_tab', addTabClickHandler);
    return false;
}

var $tabs = $("#tabs").tabs({
    add: function(event, ui) {
        $tabs
            .tabs('select', '#' + ui.panel.id)
            .tabs($tabs.tabs('option', 'selected'));
    }
});

$tabs
    .on('click', 'a.dialog', dialogOpenClickHandler)
    .on('click', 'a.add_tab', addTabClickHandler);

Respondido 02 Feb 12, 14:02

Thanks for your thoughts. Actually, that was my original approach, but I guess that doesn't scale well performance-wise. My real app has many events that have to be registered for elements that can both occur on the original page and inside newly generated tabs. Binding everything to body looked like overkill (and I noticed memory increasing significantly for Firefox), so I tried to scale down. - rvdb

Besides, I don't see any difference in page structure between dialogs generated in the first page and an added tab: ambos move their target element to body. Still, 'add_tab' links do work inside dialogs generated on the original page; I would expect this would either work on all tabs, or on none. Do you perhaps see another solution besides binding to body (e.g. can one specify the container a dialog is attached to?)? - rvdb

I'm not very fond to delegating to body neither. I will think of something and come back to you ok ? - Didier Ghys

Thanks, Didier. In the meantime, I guess I have found a workaround (though I still don't fully understand why there was an issue). - rvdb

I've come with a better solution, limiting the event delegation to the tab container and the dialog content div. - Didier Ghys

Meanwhile I've come up with a workaround that just registers the event handlers once on the 'tabs' container and appends the dialog to the 'tabs' container upon creation. My initial code could thus be trimmed down to:

// tabs: upon creation, register clicks on nested $('a.dialog') and $('a.add_tab') to open new JQueryUI dialogs / tabs 
var $tabs = $( "#tabs" ).tabs({
  create: function(event, ui) {
    $(this)
      .on('click', 'a.dialog', function(){
        // dialog: upon creation, move the dialog to the tab to ensure delegated event registration
        $($(this).attr('href')).dialog().parent('.ui-dialog').appendTo($tabs);
        return false;
      })
      .on('click', 'a.add_tab', function(){
        $tabs.tabs( "add", $(this).attr('href'), $(this).attr('href'));
        $('.ui-dialog-content').dialog('close');
        return false;
      })
  }
});

Respondido 02 Feb 12, 20:02

Well, this is just another way of achieving the same thing actually. Anyway, you still don't have to make all these calls with .on(). Just do it once on $tabs container. Also it's double work to do .each() to close the dialogs, the plugin itself will work on all the elements on the matched set, just do $('.ui-dialog-content').dialog('close');. - Didier Ghys

"Didier's comments helped me to answer my own question"... so you're actually planning on accepting your own answer here ??? - Didier Ghys

Ahh, sorry. I totally missed the edit you made to your original question before posting this. Really, apologies, will fix what's needed. - rvdb

I removed the body binding hours ago indeed ;-) - Didier Ghys

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.