Javascript - Create Dynamic Element then Do Something on Element's Click - javascript

How can I call a click event listener on a dynamically created element rendered in the DOM?
I have some scripts that dynamically create elements in the DOM, one of them being a button/a. I would like that button/a to do something once the user clicks it. Right now nothing happens but if I add a setTimeout on the things to happen upon a click, then it kind of works - only let's me do the something on the first element's click (button/a). However I can't rely on a setTimeout to make this chunk of code work.
Here is more or less what I have without the setTimeout method:
// This triggers the whole process
var mainBtn = document.querySelector('.mainBtn');
mainBtn.addEventListener('click', function(e){
e.preventDefault();
mainFunc();
});
// This creates and renders dynamic content in DOM
function mainFunc(){
var out = document.querySelector('.outputWrapper');
var mainArr = ['something ', 'another ', 'else ', 'last one.'];
var div = document.createElement("div");
var btn = document.createElement("a");
var btnText = document.createTextNode("Click Me");
btn.appendChild(btnText);
btn.className = "clickMeBtn";
for(a of probArr){
div.append(a);
div.append(btn);
}
out.append(div);
}
// This is what should happen on button/a click
var clickedBtn = document.querySelector('.clickMeBtn');
if( clickedBtn != null ){
clickedBtn.addEventListener('click', function(e){
e.preventDefault();
console.log('click');
});
}
Here's with the setTimeout method:
// This triggers the whole process
var mainBtn = document.querySelector('.mainBtn');
mainBtn.addEventListener('click', function(e){
e.preventDefault();
mainFunc();
});
// This creates and renders dynamic content in DOM
function mainFunc(){
var out = document.querySelector('.outputWrapper');
var mainArr = ['something ', 'another ', 'else ', 'last one.'];
var div = document.createElement("div");
var btn = document.createElement("a");
var btnText = document.createTextNode("Click Me");
btn.appendChild(btnText);
btn.className = "clickMeBtn";
for(a of probArr){
div.append(a);
div.append(btn);
}
out.append(div);
}
// This is what should happen on button/a click
setTimeout(function(){
var clickedBtn = document.querySelector('.clickMeBtn');
if( clickedBtn != null ){
clickedBtn.addEventListener('click', function(e){
e.preventDefault();
console.log('click');
});
}
}, 10000);
Again this kind of works...it let's me click only on the first instance of the clickedBtn variable.
Any suggestions on how to make this idea work?
Thanks a lot!!

document.querySelector('.clickMeBtn'); returns the first found element, or null.
Attach click event handler when you create the anchor element:
var btn = document.createElement("a");
btn.addEventListener('click', function(e){
e.preventDefault();
console.log('click');
});

I am not 100% sure of what is your question. But the problem I can see is that you cannot bind listeners to elements that are not yet created in the DOM.
So I can see 3 options here:
1- You build a wrapper on top of document.createElement() and a wrapper on top of addEventListener to bind the events to the elements after they are created. For example you build a map of event listeners to begin with, with the 'element selector' as Key and function to call as Value. Then you do a lookup of the listener once the element has been created and you bind it to it with addEventListener.
2- You use JQuery on() method like this:
// define the click handler for all buttons
$( document ).on( "click", "button", function() {
alert( "Button Clicked!" )
});
/* ... some time later ... */
// dynamically add another button to the page
$( "html" ).append( "<button>Click Alert!</button>" );
Source: this JQuery script is from [here][1]
(EDIT) 3- you just bind it after creation, as suggested. Although I thought you wanted to do more advanced stuff, like dynamically add elements asynchronously from the listeners.

Related

Not able to alert/access the changed id after cloning

My code is :
var draggedElement = this.template.querySelector("[id='"+divId+"']");
var cln = draggedElement.cloneNode(true,true);
cln.classList.add('completed');
cln.classList.add('box-height');
cln.id='clone-'+divId;
event.target.appendChild(cln);
Now if I want to alert the event target id on the onclick event it gives empty result.
Trying to use like this but did not work.
const btn = this.template.querySelector("[id='clone-"+divId+"']");
btn.addEventListener('click', function(event){
console.log('Button Clicked');
console.log(event.target.id);
});
Can anyone please help? Thanks in advance.
You are probably binding the event handler before the dragged element is created (which I suppose is done in response of a user action).
Instead of playing with id attributes, just bind the event handler at the moment you add the cloned element to the document. At that time you have the reference to the cloned element, so you can just add the event listener to it.
Little, simplified, demo:
var draggedElement = document.querySelector("#test");
var cln = draggedElement.cloneNode(true,true);
cln.removeAttribute("id"); // Don't use `id`
cln.textContent = "cloned " + cln.textContent;
document.body.appendChild(cln);
cln.addEventListener('click', function(event){
console.log('Cloned button Clicked');
});
<button id="test">test</button>
Event Delegation
Although I would strongly advise against using dynamically generated id attributes, if you really require to identify elements by such id attributes, then use event delegation:
setTimeout(function () { // In reality this would be some drag event handler
var draggedElement = document.querySelector("#test");
var cln = draggedElement.cloneNode(true,true);
cln.id = "cloned-" + cln.id; // Bad practice
cln.textContent = "cloned " + cln.textContent;
document.body.appendChild(cln);
}, 500);
// Event delegation
document.body.addEventListener('click', function(event){
if (event.target.id = "cloned-test") {
console.log('Cloned button Clicked');
}
});
<button id="test">test</button>

how to recall a jquery function after html change

I am trying to figure out why my function stopped working after I changed html code.
I have a div:
<div class="float">
<div class="box" data-speed="3" data-direction="X"><h1>Hola</h1></div>
<div class="box" data-speed="2" data-direction="X"><h1>chau</h1></div>
</div>
And the jquery code :
$(function() {
$('.box').moveIt();
});
//move elements in different speeds
$.fn.moveIt = function () {
var win = $(window);
var it = $(this);
var instances = [];
$(this).each(function (){
instances.push(new moveItItem($(this)));
});
$('.parallax').on('scroll', function() {
instances.forEach(function(inst){
var wrap = inst.el.parents('.float');
var scrol = win.scrollTop()-wrap.offset().top;
inst.update(scrol);
});
});
}
var moveItItem = function(el){
this.el = $(el);
this.speed = parseInt(this.el.attr('data-scroll-speed'));
this.direction = this.el.attr('data-direction');
};
moveItItem.prototype.update = function(scrollTop){
var pos = scrollTop / this.speed;
this.el.css('transform', 'translate'+this.direction+'(' + -pos + 'px)');
};
ok until here everything working, when I scroll the elements .box translate accordingly.
But now I am trying to modify the html in class .float after an ajax call
//after ajax
$.ajax({
url: 'do_content.php'
}).done(function(result) {
//result = <div class="box" data-speed="3" data-direction="X"><h1>Como estas?</h1></div>
$('.float').html(result);
});
After when I fired the scroll again the function appear to look broken and I got this message:
Uncaught TypeError: Cannot read property 'top' of undefined
at http://localhost/ophelia/public/js/control.js?v=1487219951:197:45
at Array.forEach (native)
at HTMLDivElement.<anonymous> (http://localhost/ophelia/public/js/control.js?v=1487219951:195:13)
at HTMLDivElement.dispatch (http://localhost/ophelia/public/utilities/jquery/jquery-3.1.1.min.js:3:10315)
at HTMLDivElement.q.handle (http://localhost/ophelia/public/utilities/jquery/jquery-3.1.1.min.js:3:8342)
I understand that this message appear only if I change the elements with class .box (I tried to change the only the h1 and it doesnt break but I want to change everything to change also the speeds)
How can I re-fire the function?
I tried to call it again with $('.box').moveIt(); but still getting the same error
I know is a long question but didnt find another way to explain my problem
This happens because the html element tied to the listener has been replaced..
Like in this fiddle here.. The alert works but after the html is changed, it doesn't. This is because the old element has been replaced by the new element.
You can use the on function in jQuery to get past this like in this fiddle
As already pointed (but maybe not so clear), the problem is that you attach an event handler using elements existing in the page in a certain moment of time (I think to the instances var). Then you substitute them, but your handler is already set on scroll for element with class .parallax and already registered using that instance of instances and so on.
One way is to rewrite your code using delegate methods.
From http://api.jquery.com/on/
Event handlers are bound only to the currently selected elements; they
must exist at the time your code makes the call to .on().
Delegated events have the advantage that they can process events from
descendant elements that are added to the document at a later time
An event-delegation approach attaches an event handler to only one
element, the tbody, and the event only needs to bubble up one level
(from the clicked tr to tbody):
$( "#dataTable tbody" ).on( "click", "tr", function() {
console.log( $( this ).text() );
});
But It may be complex as you should deeply restructure your code.
Otherwise you could rewrite your function as follows (sorry I can't make fiddles)
$(function() {
$('.parallax').moveIt();
});
//move elements in different speeds
$.fn.moveIt = function () {
var win = $(window);
var it = $(this);
//REMOVED
//var instances = [];
// $(this).each(function (){
// instances.push(new moveItItem($(this)));
// });
$(this).on('scroll', function() {
$('.box').each(function(){
var inst=new moveItItem($(this));
var wrap = inst.el.parents('.float');
var scrol = win.scrollTop()-wrap.offset().top;
inst.update(scrol);
});
});
}
...... and so on
you could bind event on div.float and go through element.children to move every .box

Jquery .on click event - removing the bound element

I have the following code:
$("#content h1").on("click", function(){
var f = $(this);
var clicked_id = f.data("id");
var children = _.filter(data, function(key){
var id = key.id.split("-")
id.pop()
id = id.join("-")
return clicked_id == id;
});
if (children.length != 0) {
$("#content").html("");
_.each(children, function(obj){
$("#content").append("<h1 data-id='"+ obj.id +"'>"+ obj.txt +"</h1>")
});
}
});
So basicly Im binding a .on "click" event to h1. On the click I clean the element containing the H1 then I add a new H1 element. Now the click does not register anymore. How should I actually be doing this so I can keep clicking?
Use on like so (event delegation):
$("#content").on("click", "h1", function() {
Now, each time you click #content, it'll check for an h1 and run the event. Your previous code only bound the handler to the h1 at runtime.
Maybe you could bind this way?
$(document).on("click", "#content h1", function(){
...
});

On click event only fire once while having same class

I have a notification system that generates all of an user's notifications into a table in my html along with a bunch of rows like these:
<td class = "archive-button" data-notice_id = "<%= $notice->notice_id()%>">Archive</td>
I then have a js script that runs everytime a "archive-button" is pressed:
$(document).on('click', '.archive-button', function(){
var notice_id = $(this).data('notice_id');
var archiveaddress = '/user/notices/archivenotice/' + notice_id;
var archived_notice = 'tr.notification-' + notice_id;
$.post(archiveaddress, {notice_id: notice_id}).done(function(){
$(archived_notice).css("background" , "#F2F2F2");
$('#num-posts').html(parseInt($('#num-posts').html(), 10) -1);
});
});
My question is: how do i make it so each button can only trigger the event once? Each class is the same as "archive-button" so when I do .one('clicl') it disables all my other that hasn't been clicked yet. I can retrieve the notice_id from perl and insert it into each tag when they're generated but I still can't reference that in my js script.
Just use data attributes as you have used to store notice-id, but now as a flag:
$(document).on('click', '.archive-button', function() {
if ($(this).data("clicked") !== true) {
$(this).data("clicked", true);
var notice_id = $(this).data('notice_id');
var archiveaddress = '/user/notices/archivenotice/' + notice_id;
var archived_notice = 'tr.notification-' + notice_id;
$.post(archiveaddress, {notice_id: notice_id}).done(function() {
$(archived_notice).css("background" , "#F2F2F2");
$('#num-posts').html(parseInt($('#num-posts').html(), 10) -1);
});
}
});
Note: You should use a real button(input or a) instead of a td tag acting as a button.
Attach your listeners to actual table cells as opposed to document:
$( 'td.archive-button' ).one( 'click', function(){
var notice_id = $(this).data('notice_id');
// etc....
});
When done in this way, each cell will get it's own click listener, thus effectively removing the need to store any current state.

Why is jQuery .click() is skipped over?

I have a small script of javascript which iterates over a set of checkboxes which grabs the name attribute and value and then convert it to json. Then I use that value to set the href of an element and then try to trigger a click.
For some reason everything seems to function properly except for the click. I successfully change the href, I console.log() a value before the .click() and after. Everything hits except for the click. The url in the href is value as I clicked it manually.
I have my script included just before the closing body tag and have it wrapped in $(document).ready(). and I do not have duplicate ID's (I viewed the rendered source to check)
Can anyone offer some insight on this?
Here is the javascript
$(document).ready(function() {
$("#multiExport" ).on('click', function(e){
e.preventDefault();
var i = 0;
var list = new Array();
$('.appSelect:checked').each(function(){
var name = $(this).attr('name');
var id = $(this).val();
list[i] = new Array(name, id);
i++;
});
var serList = JSON.stringify(list);
console.log(serList);
var webRoot = $("#webRoot").text();
$("#exportLink").attr('href', webRoot+"/admin/admin_export_multiExport.php?emailList="+serList); //hits
console.log('1'); //hits
$("#exportLink").click(); //this line never executes
console.log('2'); //hits
});
});
$(selector).click() won't actually follow the link the way clicking on it with your mouse will. If that's what you want, you should unwrap the jquery object from the element.
$(selector)[0].click();
Otherwise, all you're doing is triggering event handlers that may or may not exist.
I may guess you need
$(document).on('click', '#multiExport', function(e){
(you can replace document by a nearest element, if you got one).
if you need dynamic click event binding.
EDIT
I would try something like that :
$(document).ready(function() {
$("#exportLink").click(function() {
window.location = $(this).attr('href');
});
$("#multiExport" ).on('click', function(e){
//whatever you want
$('#exportLink').attr('href', 'something').trigger('click');
});
});
$("#exportLink").click(); // this would launch the event.
I must admit I am very surprised that the .click() does not work.
If the idea is to load the page, then the alternative is
$(function() {
$("#multiExport" ).on('click', function(e){
e.preventDefault();
var list = [];
$('.appSelect:checked').each(function(){
var name = $(this).attr('name');
var val = $(this).val();
list.push([name, val]);
});
var serList = JSON.stringify(list);
var webRoot = $("#webRoot").text();
location=webRoot+"/admin/admin_export_multiExport.php?emailList="+serList;
});
});

Categories