Estou tentando inserir dados html dinamicamente em uma lista que é criada dinamicamente, mas quando tento anexar um evento onclick para o botão que é criado dinamicamente, o evento não está disparando. A solução seria muito apreciada.
Código Javascript:
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('btnSubmit').addEventListener('click', function () {
var name = document.getElementById('txtName').value;
var mobile = document.getElementById('txtMobile').value;
var html = '<ul>';
for (i = 0; i < 5; i++) {
html = html + '<li>' + name + i + '</li>';
}
html = html + '</ul>';
html = html + '<input type="button" value="prepend" id="btnPrepend" />';
document.getElementsByTagName('form')[0].insertAdjacentHTML('afterend', html);
});
document.getElementById('btnPrepend').addEventListener('click', function () {
var html = '<li>Prepending data</li>';
document.getElementsByTagName('ul')[0].insertAdjacentHTML('afterbegin', html);
});
});
Código HTML:
<form>
<div class="control">
<label>Name</label>
<input id="txtName" name="txtName" type="text" />
</div>
<div class="control">
<label>Mobile</label>
<input id="txtMobile" type="text" />
</div>
<div class="control">
<input id="btnSubmit" type="button" value="submit" />
</div>
</form>
javascript
Manju
fonte
fonte
Respostas:
Isso se deve ao fato de que seu elemento é criado dinamicamente. Você deve usar a delegação de eventos para lidar com o evento.
document.addEventListener('click',function(e){ if(e.target && e.target.id== 'brnPrepend'){ //do something } });
jquery torna mais fácil:
$(document).on('click','#btnPrepend',function(){//do something})
Aqui está um artigo sobre delegação de eventos, artigo sobre delegação de eventos
fonte
Existe uma solução alternativa, capturando cliques
document.body
e, em seguida, verificando o destino do evento.document.body.addEventListener( 'click', function ( event ) { if( event.srcElement.id == 'btnSubmit' ) { someFunc(); }; } );
fonte
Você deve anexar o evento após inserir os elementos, assim você não anexa um evento global em seu,
document
mas um evento específico nos elementos inseridos.por exemplo
document.getElementById('form').addEventListener('submit', function(e) { e.preventDefault(); var name = document.getElementById('txtName').value; var idElement = 'btnPrepend'; var html = ` <ul> <li>${name}</li> </ul> <input type="button" value="prepend" id="${idElement}" /> `; /* Insert the html into your DOM */ insertHTML('form', html); /* Add an event listener after insert html */ addEvent(idElement); }); const insertHTML = (tag = 'form', html, position = 'afterend', index = 0) => { document.getElementsByTagName(tag)[index].insertAdjacentHTML(position, html); } const addEvent = (id, event = 'click') => { document.getElementById(id).addEventListener(event, function() { insertHTML('ul', '<li>Prepending data</li>', 'afterbegin') }); }
<form id="form"> <div> <label for="txtName">Name</label> <input id="txtName" name="txtName" type="text" /> </div> <input type="submit" value="submit" /> </form>
fonte
javascript document.addEventListener('click',function(e){ if(e.target && e.target.id== 'brnPrepend'){ //do something } });
A diferença está em como você cria e anexa elementos no DOM.
Se você criar um elemento via
document.createElement
, adicione um ouvinte de evento e anexe-o ao DOM. Seus eventos serão disparados.Se você criar um elemento como uma string como este: html + = "<li> test </li>" `, o elemento é tecnicamente apenas uma string. Strings não podem ter ouvintes de eventos.
Uma solução é criar cada elemento com
document.createElement
e, em seguida, adicioná-los a um elemento DOM diretamente.// Sample let li = document.createElement('li') document.querySelector('ul').appendChild(li)
fonte
Você pode fazer algo semelhante a isto:
// Get the parent to attatch the element into var parent = document.getElementsByTagName("ul")[0]; // Create element with random id var element = document.createElement("li"); element.id = "li-"+Math.floor(Math.random()*9999); // Add event listener element.addEventListener("click", EVENT_FN); // Add to parent parent.appendChild(element);
fonte
var __ = function(){ this.context = []; var self = this; this.selector = function( _elem, _sel ){ return _elem.querySelectorAll( _sel ); } this.on = function( _event, _element, _function ){ this.context = self.selector( document, _element ); document.addEventListener( _event, function(e){ var elem = e.target; while ( elem != null ) { if( "#"+elem.id == _element || self.isClass( elem, _element ) || self.elemEqal( elem ) ){ _function( e, elem ); } elem = elem.parentElement; } }, false ); }; this.isClass = function( _elem, _class ){ var names = _elem.className.trim().split(" "); for( this.it = 0; this.it < names.length; this.it++ ){ names[this.it] = "."+names[this.it]; } return names.indexOf( _class ) != -1 ? true : false; }; this.elemEqal = function( _elem ){ var flg = false; for( this.it = 0; this.it < this.context.length; this.it++ ){ if( this.context[this.it] === _elem && !flg ){ flg = true; } } return flg; }; } function _( _sel_string ){ var new_selc = new __( _sel_string ); return new_selc; }
Agora você pode registrar eventos como,
_( document ).on( "click", "#brnPrepend", function( _event, _element ){ console.log( _event ); console.log( _element ); // Todo });
Suporte de navegador
chrome - 4.0, Edge - 9.0, Firefox - 3.5 Safari - 3.2, Opera - 10.0 e superior
fonte
Eu criei uma pequena biblioteca para ajudar com isso: Fonte da biblioteca no GitHub
<script src="dynamicListener.min.js"></script> <script> // Any `li` or element with class `.myClass` will trigger the callback, // even elements created dynamically after the event listener was created. addDynamicEventListener(document.body, 'click', '.myClass, li', function (e) { console.log('Clicked', e.target.innerText); }); </script>
A funcionalidade é semelhante a jQuery.on ().
A biblioteca usa o método Element.matches () para testar o elemento de destino em relação ao seletor fornecido. Quando um evento é disparado, o retorno de chamada só é chamado se o elemento de destino corresponder ao seletor fornecido.
fonte
Eu sei que o assunto é muito antigo, mas me dei alguns minutos para criar um código muito útil que funcione bem e muito fácil de usar puro
JAVASCRIPT
. Aqui está o código com um exemplo simples:String.prototype.addEventListener=function(eventHandler, functionToDo){ let selector=this; document.body.addEventListener(eventHandler, function(e){ e=(e||window.event); e.preventDefault(); const path=e.path; path.forEach(function(elem){ const selectorsArray=document.querySelectorAll(selector); selectorsArray.forEach(function(slt){ if(slt==elem){ if(typeof functionToDo=="function") functionToDo(el=slt, e=e); } }); }); }); } // And here is how we can use it actually ! "input[type='number']".addEventListener("click", function(element, e){ console.log( e ); // Console log the value of the current number input });
<input type="number" value="25"> <br> <input type="number" value="15"> <br><br> <button onclick="addDynamicInput()">Add a Dynamic Input</button> <script type="text/javascript"> function addDynamicInput(){ const inpt=document.createElement("input"); inpt.type="number"; inpt.value=Math.floor(Math.random()*30+1); document.body.prepend(inpt); } </script>
fonte
Fiz uma função simples para isso.
A
_case
função permite que você não apenas obtenha o destino, mas também o elemento pai onde vincula o evento.A função de retorno de chamada retorna o evento que contém o target (
evt.target
) e o elemento pai que corresponde ao seletor (this
). Aqui você pode fazer o que precisa depois que o elemento é clicado.Eu ainda não decidi o que é melhor, o
if-else
ou oswitch
var _case = function(evt, selector, cb) { var _this = evt.target.closest(selector); if (_this && _this.nodeType) { cb.call(_this, evt); return true; } else { return false; } } document.getElementById('ifelse').addEventListener('click', function(evt) { if (_case(evt, '.parent1', function(evt) { console.log('1: ', this, evt.target); })) return false; if (_case(evt, '.parent2', function(evt) { console.log('2: ', this, evt.target); })) return false; console.log('ifelse: ', this); }) document.getElementById('switch').addEventListener('click', function(evt) { switch (true) { case _case(evt, '.parent3', function(evt) { console.log('3: ', this, evt.target); }): break; case _case(evt, '.parent4', function(evt) { console.log('4: ', this, evt.target); }): break; default: console.log('switch: ', this); break; } })
#ifelse { background: red; height: 100px; } #switch { background: yellow; height: 100px; }
<div id="ifelse"> <div class="parent1"> <div class="child1">Click me 1!</div> </div> <div class="parent2"> <div class="child2">Click me 2!</div> </div> </div> <div id="switch"> <div class="parent3"> <div class="child3">Click me 3!</div> </div> <div class="parent4"> <div class="child4">Click me 4!</div> </div> </div>
Espero que ajude!
fonte
Eu descobri que a solução postada por jillykate funciona, mas apenas se o elemento de destino for o mais aninhado. Se este não for o caso, isso pode ser corrigido iterando sobre os pais, ou seja,
function on_window_click(event) { let e = event.target; while (e !== null) { // --- Handle clicks here, e.g. --- if (e.getAttribute(`data-say_hello`)) { console.log("Hello, world!"); } e = e.parentElement; } } window.addEventListener("click", on_window_click);
Observe também que podemos manipular eventos por qualquer atributo ou anexar nosso ouvinte em qualquer nível. O código acima usa um atributo personalizado e
window
. Duvido que haja alguma diferença pragmática entre os vários métodos.fonte