2011-08-09 29 views
7

Tengo una situación en la que en un momento los contenidos html de un div se cambian a otra cosa, y luego se cambian de nuevo. Algunos de los controles de jquery ui se están comportando mal. Reduje el problema al siguiente fragmento, que básicamente muestra que el controlador de eventos asociado con el botón ya no se dispara. Supongo que fueron basura recolectada en algún momento cuando ya no estaban. Entonces mi pregunta es:javascript event missing after html reset

¿Cómo evito que los manejadores de eventos sean basura recolectada cuando faltan desde el DOM?

Entiendo que podría reasignar la función click(), pero como estoy usando una biblioteca externa (jquery ui), realmente no sé qué está haciendo con mis controles. Solo quiero que sus eventos sean restaurados como lo fueron antes.

<div id="container"> 
    <p>This container has a button, which will forget its click()...</p> 
    <input id="testbutton" type="button" value="Click Me!"/> 
</div> 

<script type="text/javascript"> 
    $(function(){ 
     $("#testbutton").click(
      function(){ 
       alert("Button has been clicked!"); 
     }) 
    }); 
</script> 

<div> 
    <p>... when this button reseats html</p> 
    <input id="actionbutton" type="button" value="Toggle"/> 
</div> 

<script type="text/javascript"> 
    var toggle = false; 
    var html; 
    $(function(){ 
     $("#actionbutton").click(
      function(){ 
       toggle = !toggle; 
       if(toggle){ 
        html = $("#container").html(); 
        $("#container").html(""); 
       } else $("#container").html(html); 
     }) 
    }); 
</script> 

Este jfiddle demuestra el problema que estoy teniendo.

+0

Y la alternativa es no sobrescribir el html en el contenedor, sino usar el método de separación para eliminarlo y luego agregarlo de nuevo al dom - http://jsfiddle.net/8S5E4/4/. Sin embargo, parece que esto no es lo que usted quería (basado en los comentarios del violín) –

Respuesta

4

Respuesta simple: utilizar .live

$(function(){ 
    $("#testbutton").live('click', 
     function(){ 
      alert("Button has been clicked!"); 
    }) 
}); 

Aquí está el violín actualización: http://jsfiddle.net/mrchief/8S5E4/1/

Explicación: Cuando modifica el DOM, los controladores de eventos adjuntos al elemento también se eliminan. El uso de .live conecta los manejadores de eventos a 'cuerpo' (y filtra por elemento DOM) para que 'viva' incluso después de eliminar/agregar el mismo elemento DOM.

Puede conseguir el mismo efecto usando .delegate también:

$(function(){ 
    $('#container').delegate('#testbutton', 'click', 
     function(){ 
      alert("Button has been clicked!"); 
    }) 
}); 

violín usando .delegate: http://jsfiddle.net/mrchief/8S5E4/6/

Yo recomiendo usar .delegate sobre .live para:

  • Puede cancelar burbujeo de eventos . Cuando usa live, el evento aparece todo el camino hasta body y luego se llama a su controlador, por lo que no hay forma de cancelar su burbujeo. (A partir de jQuery 1.4, esto se puede evitar utilizando un argumento de contexto: http://api.jquery.com/live/)
  • Dado que se está conectando a un elemento DOM específicamente, no se invoca cada vez que se hace clic en cualquier otro elemento DOM en el cuerpo y por lo tanto, es más eficiente, ya que tiene que hacer menos trabajo ahora.

Aquí hay un gran artículo que explica maravillosamente el differences between live, delegate and bind y analiza los matices de cada enfoque. Gracias a @andyb por señalar este enlace.

+0

¡Muchas gracias por una respuesta muy completa! Todavía tengo un problema, porque en realidad no soy yo quien hace los eventos de click() en primer lugar, es jquery ui. Lo que haré es publicar otra pregunta y presentar ese tema en particular allí. – Gleno

+0

¡Me alegra ayudar! ¡No olvides aceptar una respuesta! – Mrchief

2

jQuery delegación de eventos al rescate

$("#container").delegate('#testbutton', 'click', 
    function(){ 
     alert("Button has been clicked!"); 
}); 

Nota: .delegate()is more efficient que .live()

+0

solo por interés: ¿El problema de la eficiencia sigue siendo el mismo? El artículo que publicó tiene más de un año y fue escrito en tiempos de jquery 1.4. ¿El jQuery - Team resolvió esto o debo comenzar a usar delegate para estos casos? – GNi33

+0

A partir de 1.4, Live toma también un argumento de contexto '$ (" div.clickme ", $ (" # contenedor ") [0]). Live (" clic ", función() { // Controlador en vivo llamado. }); ': http://api.jquery.com/live/ – Mrchief

+1

@ GNi33, vea la respuesta de @ Mrchief por algunas razones más por las cuales el delegado es mejor, también [un artículo más reciente] (http://www.alfajango.com/blog)/the-difference-between-jquerys-bind-live-and-delegate /) en la diferencia. Dicho esto, estoy de acuerdo en que el artículo vinculado es antiguo y que el equipo jQuery podría haber ajustado '.live()' desde 1.4 – andyb

2

Esto está ocurriendo porque estás unirse a un evento para #testbutton, que vive en el interior de #container el documento listo. Cuando borre el código HTML de #container, también perderá ese evento de clic enlazado #testbutton. Más bien, el elemento al que hacen referencia las referencias al evento click ya no existe.

Si desea que el evento de botón persista, intente utilizar live() vinculando o delegate() el evento de un elemento primario.


Usando live()

$(function(){ 
    $("#testbutton").live("click", function() { 
     alert("Button has been clicked!"); 
    }) 
}); 

Adjuntar un manejador para el evento de todos los elementos que coinciden con el selector de corriente, ahora y en el futuro.


Usando delegate()

$(function(){ 
    $("#container").delegate("#testbutton", "click", function() { 
     alert("Button has been clicked!"); 
    }) 
}); 

Adjuntar un manejador de uno o más eventos para todos los elementos que coinciden con el selector, ahora o en el futuro, en base a un conjunto específico de elementos de raíz.