¿Vinculación de eventos en elementos creados dinámicamente?

Tengo un poco de código donde estoy recorriendo todos los cuadros de selección en una página y enlazando un .hover evento para que hagan un poco de jugueteo con su ancho en mouse on/off.

Esto sucede en la página lista y funciona bien.

El problema que tengo es que los cuadros de selección que agregue a través de Ajax o DOM después del ciclo inicial no tendrán el evento vinculado.

He encontrado este complemento (Complemento de consulta en vivo de jQuery), pero antes de agregar otros 5k a mis páginas con un complemento, quiero ver si alguien conoce una forma de hacerlo, ya sea con jQuery directamente o mediante otra opción.

preguntado Oct 14 '08, 21:10

23 Respuestas

A partir de jQuery 1.7 Deberías usar jQuery.fn.on con el parámetro selector lleno:

$(staticAncestors).on(eventName, dynamicChild, function() {});

Explicación:

Esto se denomina delegación de eventos y funciona de la siguiente manera. El evento está adjunto a un padre estático (staticAncestors) del elemento que debe manipularse. Este controlador jQuery se activa cada vez que el evento se activa en este elemento o en uno de los elementos descendientes. El controlador luego verifica si el elemento que desencadenó el evento coincide con su selector (dynamicChild). Cuando hay una coincidencia, se ejecuta su función de controlador personalizado.


Anterior a eso, el enfoque recomendado era utilizar live():

$(selector).live( eventName, function(){} );

Sin embargo, live() fue desaprobado en 1.7 a favor de on()y eliminado por completo en 1.9. La live() firma:

$(selector).live( eventName, function(){} );

... se puede reemplazar con lo siguiente on() firma:

$(document).on( eventName, selector, function(){} );

Por ejemplo, si su página estaba creando elementos dinámicamente con el nombre de la clase dosomething vincularías el evento a un padre que ya existe (este es el meollo del problema aquí, necesita algo que exista para enlazar, no enlazar con el contenido dinámico), esto puede ser (y la opción más fácil) es document. Aunque ten en cuenta document puede que no sea la opción más eficiente.

$(document).on('mouseover mouseout', '.dosomething', function(){
    // what you want to happen when mouseover and mouseout 
    // occurs on elements that match '.dosomething'
});

Cualquier padre que exista en el momento en que el evento está vinculado está bien. Por ejemplo

$('.buttons').on('click', 'button', function(){
    // do something here
});

se aplicaría a

<div class="buttons">
    <!-- <button>s that are generated dynamically and added here -->
</div>

Respondido 10 Jul 20, 13:07

Tenga en cuenta que el método en vivo solo funciona para ciertos eventos y no para otros, como los metadatos cargados. (Consulte la sección de advertencias en la documentación). sam duton

Obtenga más información sobre la delegación de eventos aquí: learn.jquery.com/events/event-delegación. - Félix Kling

¿Alguna forma de lograr esto con puro javascript / vanilla js? - carnero patra

@Ramswaroop todo lo que pueda hacer en jQuery se puede lograr sin jQuery. Aquí hay un buen ejemplo de delegación de eventos sin jQuery - David

@dave Me pregunto por qué la respuesta que señaló no aparece aquí. Eli ha pedido claramente una solución sin ningún complemento si es posible. - carnero patra

Hay una buena explicación en la documentación de jQuery.fn.on.

En breve:

Los controladores de eventos están vinculados solo a los elementos seleccionados actualmente; deben existir en la página en el momento en que su código realiza la llamada a .on().

Así, en el siguiente ejemplo #dataTable tbody tr debe existir antes de que se genere el código.

$("#dataTable tbody tr").on("click", function(event){
    console.log($(this).text());
});

Si se inyecta HTML nuevo en la página, es preferible utilizar eventos delegados para adjuntar un controlador de eventos, como se describe a continuación.

Eventos delegados tienen la ventaja de que pueden procesar eventos de elementos descendientes que se agregan al documento en un momento posterior. Por ejemplo, si la tabla existe, pero las filas se agregan dinámicamente usando código, lo siguiente lo manejará:

$("#dataTable tbody").on("click", "tr", function(event){
    console.log($(this).text());
});

Además de su capacidad para manejar eventos en elementos descendientes que aún no se han creado, otra ventaja de los eventos delegados es su potencial para una sobrecarga mucho menor cuando se deben monitorear muchos elementos. En una tabla de datos con 1,000 filas en su tbody, el primer ejemplo de código adjunta un controlador a 1,000 elementos.

Un enfoque de eventos delegados (el segundo ejemplo de código) adjunta un controlador de eventos a un solo elemento, el tbody, y el evento solo necesita subir un nivel (desde el clic tr a tbody).

Nota: Los eventos delegados no funcionan para SVG.

Respondido 30 Abr '17, 22:04

esta sería una respuesta mejor aceptada porque sería más rápido delegar desde la tabla específica en lugar de todo el camino desde el documento (el área de búsqueda sería mucho más pequeña) - sanjay

@msanjay: aunque se prefiere orientar la búsqueda más cerca de los elementos, la diferencia entre la búsqueda y la velocidad es muy pequeña en la práctica. Tendría que hacer clic 50,000 veces por segundo para notar algo :) - Codificación ido

¡Gracias! Esto soluciona mi problema. Esta simple declaración fue mi problema, "Los controladores de eventos están vinculados solo a los elementos seleccionados actualmente; deben existir en la página en el momento en que su código hace la llamada a ..." - dennis fazekas

Esto es una JavaScript puro solución sin bibliotecas ni complementos:

document.addEventListener('click', function (e) {
    if (hasClass(e.target, 'bu')) {
        // .bu clicked
        // Do your thing
    } else if (hasClass(e.target, 'test')) {
        // .test clicked
        // Do your other thing
    }
}, false);

sin que importe hasClass is

function hasClass(elem, className) {
    return elem.className.split(' ').indexOf(className) > -1;
}

Demo en vivo

El crédito es para Dave y Sime Vidas

Usando JS más moderno, hasClass se puede implementar como:

function hasClass(elem, className) {
    return elem.classList.contains(className);
}

Respondido 27 Feb 18, 20:02

Puedes utilizar Elemento.classList en lugar de dividir - eugenio konkov

@EugenKonkov Element.classList no es compatible con navegadores antiguos. Por ejemplo, IE <9. - carnero patra

Un buen artículo sobre cómo hacer las cosas usando un script vanilla en lugar de jQuery - toddmotto.com/… - carnero patra

¿qué tal burbujear? ¿Qué pasa si el evento de clic sucedió en un elemento secundario del elemento que le interesa? - Andreas Trantidis

Puede agregar eventos a los objetos cuando los crea. Si está agregando los mismos eventos a varios objetos en diferentes momentos, la creación de una función con nombre podría ser el camino a seguir.

var mouseOverHandler = function() {
    // Do stuff
};
var mouseOutHandler = function () {
    // Do stuff
};

$(function() {
    // On the document load, apply to existing elements
    $('select').hover(mouseOverHandler, mouseOutHandler);
});

// This next part would be in the callback from your Ajax call
$("<select></select>")
    .append( /* Your <option>s */ )
    .hover(mouseOverHandler, mouseOutHandler)
    .appendTo( /* Wherever you need the select box */ )
;

Respondido 22 Abr '16, 22:04

Simplemente puede envolver su llamada de enlace de eventos en una función y luego invocarla dos veces: una vez en el documento listo y una vez después de su evento que agrega los nuevos elementos DOM. Si lo hace, querrá evitar vincular el mismo evento dos veces en los elementos existentes, por lo que necesitará desvincular los eventos existentes o (mejor) solo vincular los elementos DOM que se crearon recientemente. El código se vería así:

function addCallbacks(eles){
    eles.hover(function(){alert("gotcha!")});
}

$(document).ready(function(){
    addCallbacks($(".myEles"))
});

// ... add elements ...
addCallbacks($(".myNewElements"))

Respondido 22 Abr '16, 22:04

Esta publicación realmente me ayudó a comprender un problema que estaba teniendo al cargar el mismo formulario y obtener 1,2,4,8,16, XNUMX, XNUMX, XNUMX, XNUMX ... presentaciones. En lugar de usar .live (), solo usé .bind () en mi devolución de llamada .load (). Problema resuelto. ¡Gracias! - lasfloresdeltiempo

Tratar de usar .live() en lugar de .bind(); El .live() se unirá .hover a su casilla de verificación después de que se ejecute la solicitud Ajax.

Respondido el 06 de Septiembre de 15 a las 21:09

El método live() quedó obsoleto en la versión 1.7 a favor de on y eliminado en la versión 1.9. - Criada

Enlace de eventos en elementos creados dinámicamente

Elemento único:

$(document.body).on('click','.element', function(e) {  });

Elemento secundario:

 $(document.body).on('click','.element *', function(e) {  });

Note el agregado *. Se activará un evento para todos los hijos de ese elemento.

Me he dado cuenta que:

$(document.body).on('click','.#element_id > element', function(e) {  });

Ya no funciona, pero funcionaba antes. He estado usando jQuery de Google CDN, pero no sé si lo cambiaron.

Respondido 22 Abr '16, 22:04

Sí, y no están diciendo (document.body) que dice ancestro, que podría ser casi cualquier cosa - hecho en sueños

Prefiero usar el selector y lo aplico en el documento.

Esto se une al documento y será aplicable a los elementos que se renderizarán después de la carga de la página.

Por ejemplo:

$(document).on("click", 'selector', function() {
    // Your code here
});

Respondido 19 Abr '20, 08:04

el selector no debe estar entre $, por lo que el formato correcto será $ (documento) .on ("clic", "selector", función () {// Su código aquí}); - piloto automático

También es inútil envolver un objeto jQuery alrededor del selector variable, cuando debe contener una cadena o un objeto Elemento que puede pasar directamente a ese argumento de on() - Rory McCrossan

Debe apuntar a vincularlo al padre estático más cercano, no al documento completo. - Marcos Baijens

Puede usar el método live () para vincular elementos (incluso los recién creados) a eventos y controladores, como el evento onclick.

Aquí hay un código de muestra que he escrito, donde puede ver cómo el método live () une los elementos elegidos, incluso los recién creados, a los eventos:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Untitled Document</title>
    </head>

    <body>
        <script src="http://code.jquery.com/jquery-latest.js"></script>
        <script src="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.16/jquery-ui.min.js"></script>

        <input type="button" id="theButton" value="Click" />
        <script type="text/javascript">
            $(document).ready(function()
                {
                    $('.FOO').live("click", function (){alert("It Works!")});
                    var $dialog = $('<div></div>').html('<div id="container"><input type ="button" id="CUSTOM" value="click"/>This dialog will show every time!</div>').dialog({
                                                                                                         autoOpen: false,
                                                                                                         tite: 'Basic Dialog'
                                                                                                     });
                    $('#theButton').click(function()
                    {
                        $dialog.dialog('open');
                        return('false');
                    });
                    $('#CUSTOM').click(function(){
                        //$('#container').append('<input type="button" value="clickmee" class="FOO" /></br>');
                        var button = document.createElement("input");
                        button.setAttribute('class','FOO');
                        button.setAttribute('type','button');
                        button.setAttribute('value','CLICKMEE');
                        $('#container').append(button);
                    });
                    /* $('#FOO').click(function(){
                                                     alert("It Works!");
                                                 }); */
            });
        </script>
    </body>
</html>

Respondido 22 Abr '16, 22:04

Otra solución es agregar el oyente al crear el elemento. En lugar de poner al oyente en el cuerpo, pones al oyente en el elemento en el momento en que lo creas:

var myElement = $('<button/>', {
    text: 'Go to Google!'
});

myElement.bind( 'click', goToGoogle);
myElement.append('body');


function goToGoogle(event){
    window.location.replace("http://www.google.com");
}

Respondido 22 Abr '16, 22:04

Tu código contiene 1 error: myElement.append('body'); debe ser myElement.appendTo('body');. Por otro lado, si no hay necesidad de seguir utilizando variables myElement es más fácil y más corto de esta manera: $('body').append($('<button/>', { text: 'Go to Google!' }).bind( 'click', goToGoogle)); - ddlab

Esto se hace mediante delegación de eventos.. El evento se vinculará en el elemento de clase contenedora, pero se delegará en el elemento de clase selectora. Así es como funciona.

$('.wrapper-class').on("click", '.selector-class', function() {
    // Your code here
});

Y HTML

<div class="wrapper-class">
    <button class="selector-class">
      Click Me!
    </button>
</div>    

#Nota: el elemento de clase contenedora puede ser cualquier cosa, por ejemplo. documento, cuerpo o su envoltorio. El envoltorio ya debería existir. Sin embargo, selector no es necesario que se presente en el momento de la carga de la página. Puede llegar más tarde y el evento se vinculará selector sin fallar.

respondido 22 nov., 20:16

Sí, pero creará propagación de eventos para elementos. debe agregar el objetivo al elemento dinámico específico y detener la propagación del evento. - Asad Ali

En caso de que vincule el mismo evento en el elemento principal y descendiente, puede detenerlo usando event.stopPropagation () - imprescindible k

Intenta así -

$(document).on( 'click', '.click-activity', function () { ... });

Respondido 06 ago 19, 08:08

Debe apuntar a vincularlo al padre estático más cercano, no al documento completo. - Marcos Baijens

Tome nota de la clase "PRINCIPAL" en el que se coloca el elemento, por ejemplo,

<div class="container">
     <ul class="select">
         <li> First</li>
         <li>Second</li>
    </ul>
</div>

En el escenario anterior, el objeto PRINCIPAL que observará jQuery es "contenedor".

Entonces básicamente tendrás nombres de elementos debajo del contenedor como ul, li y select:

$(document).ready(function(e) {
    $('.container').on( 'click',".select", function(e) {
        alert("CLICKED");
    });
 });

Respondido 22 Abr '16, 22:04

podrías usar

$('.buttons').on('click', 'button', function(){
    // your magic goes here
});

or

$('.buttons').delegate('button', 'click', function() {
    // your magic goes here
});

estos dos métodos son equivalentes pero tienen un orden de parámetros diferente.

ver: Evento de delegado de jQuery

Respondido 11 ago 16, 17:08

delegate() ahora está en desuso. No lo uses. - Rory McCrossan

Puede adjuntar un evento al elemento cuando se crea dinámicamente usando jQuery(html, attributes).

A partir de jQuery 1.8, cualquier método de instancia de jQuery (un método de jQuery.fn) se puede utilizar como una propiedad del objeto pasado al segundo parámetro:

function handleDynamicElementEvent(event) {
  console.log(event.type, this.value)
}
// create and attach event to dynamic element
jQuery("<select>", {
    html: $.map(Array(3), function(_, index) {
      return new Option(index, index)
    }),
    on: {
      change: handleDynamicElementEvent
    }
  })
  .appendTo("body");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>

Respondido 13 ago 17, 13:08

A continuación, se explica por qué los elementos creados dinámicamente no responden a los clics:

var body = $("body");
var btns = $("button");
var btnB = $("<button>B</button>");
// `<button>B</button>` is not yet in the document.
// Thus, `$("button")` gives `[<button>A</button>]`.
// Only `<button>A</button>` gets a click listener.
btns.on("click", function () {
  console.log(this);
});
// Too late for `<button>B</button>`...
body.append(btnB);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>

Como solución alternativa, debe escuchar todos los clics y verificar el elemento de origen:

var body = $("body");
var btnB = $("<button>B</button>");
var btnC = $("<button>C</button>");
// Listen to all clicks and
// check if the source element
// is a `<button></button>`.
body.on("click", function (ev) {
  if ($(ev.target).is("button")) {
    console.log(ev.target);
  }
});
// Now you can add any number
// of `<button></button>`.
body.append(btnB);
body.append(btnC);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>

Esto se llama "Delegación de eventos". Buenas noticias, es una función incorporada en jQuery :-)

var i = 11;
var body = $("body");
body.on("click", "button", function () {
  var letter = (i++).toString(36).toUpperCase();
  body.append($("<button>" + letter + "</button>"));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>

Respondido 19 Oct 17, 13:10

También es posible asignar var body = $("body").on( ... ); directamente. - Sebastian simon

Cualquier pno existe en el momento en que el evento está vinculado y si su página fue crear elementos dinámicamente con el nombre de la clase . vincularías el evento a un padre que ya existe

$(document).ready(function(){
  //Particular Parent chield click
  $(".buttons").on("click","button",function(){
    alert("Clicked");
  });  
  
  //Dynamic event bind on button class  
  $(document).on("click",".button",function(){
    alert("Dymamic Clicked");
  });
  $("input").addClass("button");  
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="buttons">
  <input type="button" value="1">
  <button>2</button>
  <input type="text">
  <button>3</button>  
  <input type="button" value="5">  
  </div>
<button>6</button>

Respondido el 18 de diciembre de 15 a las 08:12

Vincula el evento a un padre que ya existe:

$(document).on("click", "selector", function() {
    // Your code here
});

Respondido el 08 de junio de 18 a las 03:06

Otra solución flexible para crear elementos y vincular eventos (fuente)

// creating a dynamic element (container div)
var $div = $("<div>", {id: 'myid1', class: 'myclass'});

//creating a dynamic button
 var $btn = $("<button>", { type: 'button', text: 'Click me', class: 'btn' });

// binding the event
 $btn.click(function () { //for mouseover--> $btn.on('mouseover', function () {
    console.log('clicked');
 });

// append dynamic button to the dynamic container
$div.append($btn);

// add the dynamically created element(s) to a static element
$("#box").append($div);

Nota: Esto creará una instancia de controlador de eventos para cada elemento. (puede afectar el rendimiento cuando se usa en bucles)

respondido 27 mar '19, 04:03

Use el .on() método de jQuery http://api.jquery.com/on/ para adjuntar controladores de eventos al elemento en vivo.

También a partir de la versión 1.9 .live() se elimina el método.

respondido 30 nov., 16:09

Prefiero que los oyentes de eventos se implementen en una forma de función modular en lugar de programar un document oyente de eventos de nivel. Entonces, me gusta a continuación. Tenga en cuenta que no puede sobrescribir un elemento con el mismo oyente de eventos, así que no se preocupe por adjuntar un oyente más de una vez, solo uno se queda.

var iterations = 4;
var button;
var body = document.querySelector("body");

for (var i = 0; i < iterations; i++) {
    button = document.createElement("button");
    button.classList.add("my-button");
    button.appendChild(document.createTextNode(i));
    button.addEventListener("click", myButtonWasClicked);
    body.appendChild(button);
}

function myButtonWasClicked(e) {
    console.log(e.target); //access to this specific button
}

Respondido el 08 de junio de 18 a las 03:06

Prefiero esta implementación; Solo tengo que configurar una devolución de llamada. William

<html>
    <head>
        <title>HTML Document</title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
    </head>

    <body>
        <div id="hover-id">
            Hello World
        </div>

        <script>
            jQuery(document).ready(function($){
                $(document).on('mouseover', '#hover-id', function(){
                    $(this).css('color','yellowgreen');
                });

                $(document).on('mouseout', '#hover-id', function(){
                    $(this).css('color','black');
                });
            });
        </script>
    </body>
</html>

Respondido el 27 de Septiembre de 17 a las 16:09

Si bien este fragmento de código puede resolver el problema, no explica por qué ni cómo responde a la pregunta. Incluya una explicación para su código, ya que eso realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo a la pregunta para los lectores en el futuro, y es posible que esas personas no conozcan los motivos de su sugerencia de código. - Dedo

Estaba buscando una solución para conseguir $.bind y $.unbind trabajando sin problemas en elementos añadidos dinámicamente.

As on() hace el truco para adjuntar eventos, con el fin de crear una desvinculación falsa en aquellos a los que vine:

const sendAction = function(e){ ... }
// bind the click
$('body').on('click', 'button.send', sendAction );

// unbind the click
$('body').on('click', 'button.send', function(){} );

contestado el 18 de mayo de 18 a las 01:05

La desvinculación no funciona, esto simplemente agrega otro evento que apunta a una función vacía ... - Fabián Bigler

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