Ligação de evento em elementos criados dinamicamente?

1677

Eu tenho um pouco de código em que estou percorrendo todas as caixas de seleção de uma página e vinculando um .hoverevento a elas para fazer um pouco de brincadeira com a largura ativada mouse on/off.

Isso acontece na página pronta e funciona muito bem.

O problema que tenho é que todas as caixas de seleção adicionadas via Ajax ou DOM após o loop inicial não terão o evento vinculado.

Eu encontrei este plug-in ( jQuery Live Query Plugin ), mas antes de adicionar mais 5k às minhas páginas com um plug-in, quero ver se alguém sabe como fazer isso, seja diretamente com o jQuery ou por outra opção.

Eli
fonte

Respostas:

2250

A partir do jQuery 1.7, você deve usar jQuery.fn.on:

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

Antes disso , a abordagem recomendada era usar live():

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

No entanto, live()foi preterido em 1,7 a favor on()e completamente removido em 1,9. A live()assinatura:

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

... pode ser substituído pela seguinte on()assinatura:

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

Por exemplo, se sua página estava criando dinamicamente elementos com o nome da classe, dosomethingvocê vincularia o evento a um pai que já existe (este é o ponto principal do problema aqui, você precisará de algo que exista para se conectar, não vincule ao conteúdo dinâmico), pode ser (e a opção mais fácil) é document. Embora tenha em mente que documentpode não ser a opção mais eficiente .

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

Qualquer pai que exista no momento em que o evento está vinculado é bom. Por exemplo

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

se aplicaria a

<div class="buttons">
    <!-- <button>s that are generated dynamically and added here -->
</div>
Popnoodles
fonte
7
Observe que o método ao vivo funciona apenas para determinados eventos e não para outros, como metadados carregados. (Veja a seção advertências na documentação.)
Sam Dutton
48
Saiba mais sobre a delegação de eventos aqui: learn.jquery.com/events/event-delegation .
Felix Kling
9
Alguma maneira de fazer isso com puro javascript / vanilla js?
Ram Patra #
15
@ Ramswaroop tudo o que você pode fazer no jQuery pode ser realizado sem o jQuery. Aqui está um bom exemplo de delegação evento sem jQuery
Dave
5
@dave Gostaria de saber por que a resposta que você apontou não está listada aqui. Eli pediu claramente uma solução sem nenhum plugin, se possível.
Ram Patra
377

Há uma boa explicação na documentação de jQuery.fn.on.

Em resumo:

Os manipuladores de eventos são vinculados apenas aos elementos selecionados no momento; eles devem existir na página no momento em que seu código faz a chamada .on().

Portanto, no exemplo a seguir #dataTable tbody trdeve existir antes que o código seja gerado.

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

Se um novo HTML estiver sendo injetado na página, é preferível usar eventos delegados para anexar um manipulador de eventos, conforme descrito a seguir.

Eventos delegados têm a vantagem de poder processar eventos de elementos descendentes que são adicionados ao documento posteriormente. Por exemplo, se a tabela existir, mas as linhas forem adicionadas dinamicamente usando o código, o seguinte será tratado:

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

Além de sua capacidade de manipular eventos em elementos descendentes que ainda não foram criados, outra vantagem dos eventos delegados é seu potencial de sobrecarga muito menor quando muitos elementos devem ser monitorados. Em uma tabela de dados com 1.000 linhas tbody, o primeiro exemplo de código anexa um manipulador a 1.000 elementos.

Uma abordagem de eventos delegados (o segundo exemplo de código) anexa um manipulador de eventos a apenas um elemento, oe tbodyo evento precisa apenas aumentar um nível (de clicado trpara tbody).

Nota: Eventos delegados não funcionam para SVG .

Ronen Rabinovici
fonte
11
this'd ser uma resposta melhor aceite porque seria mais rápido para delegar a partir da tabela específica, em vez de todo o caminho do documento (a área de pesquisa seria muito menor)
msanjay
5
@ msanjay: Embora seja preferível segmentar a pesquisa mais próxima dos elementos, na prática, a diferença de velocidade / pesquisa é muito pequena. Você teria que clicar 50.000 vezes por segundo para notar nada :)
Longe de Codificação
2
Essa abordagem pode ser aplicada ao tratamento de cliques da caixa de seleção em uma linha, alterando trpara um seletor apropriado, como 'input[type=checkbox]', e isso seria tratado automaticamente para as linhas recém-inseridas?
JoeBrockhaus
1
Obrigado! Isso resolve meu problema. Esta simples declaração era o meu problema "manipuladores de eventos estão vinculados apenas para os elementos atualmente selecionados, eles devem existir na página no momento seu código faz a chamada para ..."
Dennis Fazekas
216

Esta é uma solução JavaScript pura , sem bibliotecas ou plugins:

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);

onde hasClassesta

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

Live demo

O crédito é para Dave e Sime Vidas

Utilizando JS mais moderno, hasClasspode ser implementado como:

function hasClass(elem, className) {
    return elem.classList.contains(className);
}
Ram Patra
fonte
6
Você pode usar element.classList em vez de divisão
Eugen Konkov
2
O @EugenKonkov Element.classListnão é suportado em navegadores antigos. Por exemplo, o IE <9.
Ram Patra
2
Um bom artigo sobre como fazer as coisas usando o script de baunilha em vez de jQuery - toddmotto.com/...
Ram Patra
2
que tal borbulhar? E se o evento click aconteceu em um filho do elemento em que você está interessado?
Andreas Trantidis
73

Você pode adicionar eventos aos objetos ao criá-los. Se você estiver adicionando os mesmos eventos a vários objetos em momentos diferentes, a criação de uma função nomeada pode ser o caminho 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 */ )
;
nickf
fonte
49

Você pode simplesmente agrupar sua chamada de ligação de evento em uma função e depois invocá-la duas vezes: uma vez no documento pronto e uma vez após o seu evento que adiciona os novos elementos DOM. Se você fizer isso, evite associar o mesmo evento duas vezes aos elementos existentes, para desassociar os eventos existentes ou (melhor) vincular apenas aos elementos DOM que foram criados recentemente. O código ficaria assim:

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

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

// ... add elements ...
addCallbacks($(".myNewElements"))
Greg Borenstein
fonte
2
Este post realmente me ajudou a entender um problema que eu estava tendo ao carregar o mesmo formulário e receber 1,2,4,8,16 ... envios. Em vez de usar .live (), apenas usei .bind () no meu retorno de chamada .load (). Problema resolvido. Obrigado!
Thomas McCabe
42

Tente usar em .live()vez de .bind(); ele .live()será vinculado .hoverà sua caixa de seleção após a execução da solicitação do Ajax.

user670265
fonte
32
O método live()foi preterido na versão 1.7 em favor one excluído na versão 1.9.
chridam
27

Ligação de evento em elementos criados dinamicamente

Elemento único:

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

Elemento filho:

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

Observe o adicionado *. Um evento será acionado para todos os filhos desse elemento.

Eu notei que:

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

Não está mais funcionando, mas estava funcionando antes. Estou usando o jQuery do Google CDN , mas não sei se eles foram alterados.

MadeInDreams
fonte
Yeap e eles não estão dizendo (document.body) sua diz ancestral wich poderia ser praticamente qualquer coisa
MadeInDreams
25

Você pode usar o método live () para vincular elementos (mesmo os recém-criados) a eventos e manipuladores, como o evento onclick.

Aqui está um código de exemplo que eu escrevi, onde você pode ver como o método live () liga elementos escolhidos, mesmo os recém-criados, a 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>
Fazi
fonte
23

Prefiro usar o seletor e aplicá-lo no documento.

Isso se liga ao documento e será aplicável aos elementos que serão renderizados após o carregamento da página.

Por exemplo:

$(document).on("click", 'selector', function() {
    // Your code here
});
Vatsal
fonte
2
o seletor não deve ser colocado entre $ e, portanto, o formato correto será $ (document) .on ("click", "selector", function () {// Seu código aqui});
autopilot
1
Também é inútil para embrulhar um objeto jQuery em torno da selectorvariável, quando se deve ou conter uma cadeia ou elemento de objeto que você pode apenas passar diretamente para que o argumento deon()
Rory McCrossan
20

Outra solução é adicionar o ouvinte ao criar o elemento. Em vez de colocar o ouvinte no corpo, você o coloca no elemento no momento em que o cria:

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

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


function goToGoogle(event){
    window.location.replace("http://www.google.com");
}
Martin Da Rosa
fonte
Seu código contém 1 erro: myElement.append('body');deve ser myElement.appendTo('body');. Por outro lado, se não houver necessidade de um maior uso de variável myElementé mais fácil e mais curto desta maneira:$('body').append($('<button/>', { text: 'Go to Google!' }).bind( 'click', goToGoogle));
ddlab
17

Tente assim -

$(document).on( 'click', '.click-activity', function () { ... });
Rohit Suthar
fonte
16

Observe a classe "MAIN" em que o elemento é colocado, por exemplo,

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

No cenário acima, o objeto PRINCIPAL que o jQuery observará é "contêiner".

Então você tem basicamente elementos nomes sob recipiente, como ul, li, e select:

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

Isso é feito pela delegação de eventos . O evento será vinculado ao elemento da classe wrapper, mas será delegado ao elemento da classe seletor. É assim que funciona.

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

Nota:

O elemento wrapper-class pode ser qualquer coisa ex. documento, corpo ou seu invólucro. O wrapper já deve existir . No entanto, selectornão precisa necessariamente ser apresentado no momento do carregamento da página. Pode ocorrer mais tarde e o evento será vinculado selector sem falhas .

Mustkeem K
fonte
14

você poderia usar

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

ou

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

esses dois métodos são equivalentes, mas têm uma ordem diferente de parâmetros.

veja: jQuery Delegate Event

Mensur Grišević
fonte
2
delegate()agora está obsoleto. Não use isso.
Rory McCrossan
14

Você pode anexar um evento ao elemento quando criado dinamicamente usando jQuery(html, attributes).

A partir do jQuery 1.8 , qualquer método da instância do jQuery (um método de jQuery.fn) pode ser usado como uma propriedade do objeto passado para o 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>

guest271314
fonte
11

Eis por que os elementos criados dinamicamente não respondem aos cliques:

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 solução alternativa, você deve ouvir todos os cliques e verificar o elemento de origem:

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>

Isso é chamado de "Delegação de Eventos". Boas notícias, é um recurso interno do 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>

folha
fonte
10

Qualquer coisa que exista no momento em que o evento é vinculado e se sua página estava criando dinamicamente elementos com o botão de nome de classe , você vincularia o evento a um pai que já 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>

Ankit Kathiriya
fonte
7

Vincule o evento a um pai que já existe:

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

Use o .on()método do jQuery http://api.jquery.com/on/ para anexar manipuladores de eventos ao elemento live.

Também a partir da versão 1.9, o .live()método é removido.

Kalpesh Patel
fonte
3

Prefiro que os ouvintes de eventos sejam implantados de forma modular, em vez de criar scripts para um documentouvinte de nível. Então, eu gosto abaixo. Observe que você não pode assinar em excesso um elemento com o mesmo ouvinte de evento; portanto, não se preocupe em anexar um ouvinte mais de uma vez - apenas um fica.

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
}

Ronnie Royston
fonte
Eu prefiro essa implementação; Eu só tenho que configurar uma chamada de volta #
William
3

Outra solução flexível para criar elementos e vincular eventos ( origem )

// 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: Isso criará uma instância do manipulador de eventos para cada elemento (pode afetar o desempenho quando usado em loops)

Prasad De Silva
fonte
0
<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>
Fakhrul Hasan
fonte
3
Embora esse trecho de código possa resolver o problema, ele não explica por que ou como responde à pergunta. Inclua uma explicação para o seu código, pois isso realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código.
Palec 30/09
0

Eu estava procurando uma solução para começar $.binde $.unbindtrabalhar sem problemas em elementos adicionados dinamicamente.

Como on () faz o truque para anexar eventos, a fim de criar um desatamento falso dos que eu vim:

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

// unbind the click
$('body').on('click', 'button.send', function(){} );
Evhz
fonte
A desvinculação não funciona, isso simplesmente acrescenta um outro evento que aponta para uma função vazia ...
Fabian Bigler