I'm trying to insert html data dynamically to a list that is dynamically created, but when i try to attach an onclick event for the button that is dynamically created the event is not firing. Solution would be really appreciated.
Javascript code:
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);
});
});
HTML Code:
<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>
This question is related to
javascript
You must attach the event after insert elements, like that you don't attach a global event on your document
but a specific event on the inserted elements.
e.g.
document.getElementById('form').addEventListener('submit', function(e) {_x000D_
e.preventDefault();_x000D_
var name = document.getElementById('txtName').value;_x000D_
var idElement = 'btnPrepend';_x000D_
var html = `_x000D_
<ul>_x000D_
<li>${name}</li>_x000D_
</ul>_x000D_
<input type="button" value="prepend" id="${idElement}" />_x000D_
`;_x000D_
/* Insert the html into your DOM */_x000D_
insertHTML('form', html);_x000D_
/* Add an event listener after insert html */_x000D_
addEvent(idElement);_x000D_
});_x000D_
_x000D_
const insertHTML = (tag = 'form', html, position = 'afterend', index = 0) => {_x000D_
document.getElementsByTagName(tag)[index].insertAdjacentHTML(position, html);_x000D_
}_x000D_
const addEvent = (id, event = 'click') => {_x000D_
document.getElementById(id).addEventListener(event, function() {_x000D_
insertHTML('ul', '<li>Prepending data</li>', 'afterbegin')_x000D_
});_x000D_
}
_x000D_
<form id="form">_x000D_
<div>_x000D_
<label for="txtName">Name</label>_x000D_
<input id="txtName" name="txtName" type="text" />_x000D_
</div>_x000D_
<input type="submit" value="submit" />_x000D_
</form>
_x000D_
I know that the topic is too old but I gave myself some minutes to create a very useful code that works fine and very easy using pure JAVASCRIPT
.
Here is the code with a simple example:
String.prototype.addEventListener=function(eventHandler, functionToDo){_x000D_
let selector=this;_x000D_
document.body.addEventListener(eventHandler, function(e){_x000D_
e=(e||window.event);_x000D_
e.preventDefault();_x000D_
const path=e.path;_x000D_
path.forEach(function(elem){_x000D_
const selectorsArray=document.querySelectorAll(selector);_x000D_
selectorsArray.forEach(function(slt){_x000D_
if(slt==elem){_x000D_
if(typeof functionToDo=="function") functionToDo(el=slt, e=e);_x000D_
}_x000D_
});_x000D_
});_x000D_
});_x000D_
}_x000D_
_x000D_
// And here is how we can use it actually !_x000D_
_x000D_
"input[type='number']".addEventListener("click", function(element, e){_x000D_
console.log( e ); // Console log the value of the current number input_x000D_
});
_x000D_
<input type="number" value="25">_x000D_
<br>_x000D_
<input type="number" value="15">_x000D_
<br><br>_x000D_
<button onclick="addDynamicInput()">Add a Dynamic Input</button>_x000D_
<script type="text/javascript">_x000D_
function addDynamicInput(){_x000D_
const inpt=document.createElement("input");_x000D_
inpt.type="number";_x000D_
inpt.value=Math.floor(Math.random()*30+1);_x000D_
document.body.prepend(inpt);_x000D_
}_x000D_
</script>
_x000D_
I have found the solution posted by jillykate works, but only if the target element is the most nested. If this is not the case, this can be rectified by iterating over the parents, i.e.
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);
Also note we can handle events by any attribute, or attach our listener at any level. The code above uses a custom attribute and window
. I doubt there is any pragmatic difference between the various methods.
You can do something similar to this:
// 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);
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;
}
Now you can register event like,
_( document ).on( "click", "#brnPrepend", function( _event, _element ){
console.log( _event );
console.log( _element );
// Todo
});
Browser Support
chrome - 4.0, Edge - 9.0, Firefox - 3.5 Safari - 3.2, Opera - 10.0 and above
There is a workaround by capturing clicks on document.body
and then checking event target.
document.body.addEventListener( 'click', function ( event ) {
if( event.srcElement.id == 'btnSubmit' ) {
someFunc();
};
} );
The difference is in how you create and append elements in the DOM.
If you create an element via document.createElement
, add an event listener, and append it to the DOM. Your events will fire.
If you create an element as a string like this: html += "<li>test</li>"`, the elment is technically just a string. Strings cannot have event listeners.
One solution is to create each element with document.createElement
and then add those to a DOM element directly.
// Sample
let li = document.createElement('li')
document.querySelector('ul').appendChild(li)
I have created a small library to help with this: Library source on 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>
The functionality is similar to jQuery.on().
The library uses the Element.matches() method to test the target element against the given selector. When an event is triggered the callback is only called if the target element matches the selector given.
I've made a simple function for this.
The _case
function allows you to not only get the target, but also get the parent element where you bind the event on.
The callback function returns the event which holds the target (evt.target
) and the parent element matching the selector (this
). Here you can do the stuff you need after the element is clicked.
I've not yet decided which is better, the if-else
or the switch
var _case = function(evt, selector, cb) {_x000D_
var _this = evt.target.closest(selector);_x000D_
if (_this && _this.nodeType) {_x000D_
cb.call(_this, evt);_x000D_
return true;_x000D_
} else { return false; }_x000D_
}_x000D_
_x000D_
document.getElementById('ifelse').addEventListener('click', function(evt) {_x000D_
if (_case(evt, '.parent1', function(evt) {_x000D_
console.log('1: ', this, evt.target);_x000D_
})) return false;_x000D_
_x000D_
if (_case(evt, '.parent2', function(evt) {_x000D_
console.log('2: ', this, evt.target);_x000D_
})) return false;_x000D_
_x000D_
console.log('ifelse: ', this);_x000D_
})_x000D_
_x000D_
_x000D_
document.getElementById('switch').addEventListener('click', function(evt) {_x000D_
switch (true) {_x000D_
case _case(evt, '.parent3', function(evt) {_x000D_
console.log('3: ', this, evt.target);_x000D_
}): break;_x000D_
case _case(evt, '.parent4', function(evt) {_x000D_
console.log('4: ', this, evt.target);_x000D_
}): break;_x000D_
default:_x000D_
console.log('switch: ', this);_x000D_
break;_x000D_
}_x000D_
})
_x000D_
#ifelse {_x000D_
background: red;_x000D_
height: 100px;_x000D_
}_x000D_
#switch {_x000D_
background: yellow;_x000D_
height: 100px;_x000D_
}
_x000D_
<div id="ifelse">_x000D_
<div class="parent1">_x000D_
<div class="child1">Click me 1!</div>_x000D_
</div>_x000D_
<div class="parent2">_x000D_
<div class="child2">Click me 2!</div>_x000D_
</div>_x000D_
</div>_x000D_
_x000D_
<div id="switch">_x000D_
<div class="parent3">_x000D_
<div class="child3">Click me 3!</div>_x000D_
</div>_x000D_
<div class="parent4">_x000D_
<div class="child4">Click me 4!</div>_x000D_
</div>_x000D_
</div>
_x000D_
Hope it helps!
Source: Stackoverflow.com