On click event only fire once while having same class - javascript

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.

Related

How to set on change as a default on page load jquery?

I have created a on change method for a select box of my project. On selecting particular option it is basically showing and hiding a div which is perfectly working fine. Now, my problem is when first time page is loading this show and hide not working for first default section of form. Can I make this onchange function also working when page load first time.
$('.contact-form').on('change', (e) => {
var selectedId = $(e.currentTarget).val();
var listofforms = $("#discount").data("display-for").split(",");
if (listofforms.indexOf(selectedId) !== -1) {
$("#discount").collapse('show');
}
else {
$("#discount").collapse('hide');
}
});
Here you go with a solution
function changeMethod(selectedId) {
var listofforms = $("#discount").data("display-for").split(",");
if (listofforms.indexOf(selectedId) !== -1) {
$("#discount").collapse('show');
}
else {
$("#discount").collapse('hide');
}
}
changeMethod($('.contact-form').val())
$('.contact-form').on('change', (e) => {
changeMethod($(e.currentTarget).val());
});
You need to move your code outside the change event, so I have kept your existing code within a method changeMethod.
Then call the method from to places
From you change event method
OnLoad of the JS file
Is it possible can I make my on change trigger on page load
Yes, you will just need to change your on change event from e.currentTarget to this as on page load e.currentTarget will be null, but this always points to the current element like:
$('.contact-form').on('change', function() {
var selectedId = $(this).val();
// Your other logic here
});
and to trigger this change event on page load, simply add .change() at last like:
$('.contact-form').on('change', function() {
var selectedId = $(this).val();
// Your other logic here
}).change(); //<---- here

How can I observe changes to my DOM and react to them with jQuery?

I have this function where I toggle a class on click, but also append HTML to an element, still based on that click.
The problem is that now, I'm not listening to any DOM changes at all, so, once I do my first click, yup, my content will be added, but if I click once again - the content gets added again, because as far as this instance of jQuery is aware, the element is not there.
Here's my code:
(function($) {
"use strict";
var closePluginsList = $('#go-back-to-setup-all');
var wrapper = $('.dynamic-container');
$('#install-selected-plugins, #go-back-to-setup-all').on('click', function(event) {
$('.setup-theme-container').toggleClass('plugins-list-enabled');
if ( !wrapper.has('.plugins-container') ){
var markup = generate_plugins_list_markup();
wrapper.append(markup);
} else {
$('.plugins-container').hide();
}
});
//Below here, there's a lot of code that gets put into the markup variable. It's just generating the HTML I'm adding.
})(jQuery);
Someone suggested using data attributes, but I've no idea how to make them work in this situation.
Any ideas?
You could just do something like adding a flag and check for it before adding your markup.
var flag = 0;
$('#install-selected-plugins, #go-back-to-setup-all').on('click', function(event) {
$('.setup-theme-container').toggleClass('plugins-list-enabled');
if ( !wrapper.has('.plugins-container') ){
var markup = generate_plugins_list_markup();
if(flag == 0){
wrapper.append(markup);
flag = 1;
}
} else {
$('.plugins-container').hide();
}
});
If you want to add element once only on click then you should make use of .one() and put logic you want to execute once only in that handler.
Example :
$(document).ready(function(){
$("p").one("click", function(){
//this will get execute once only
$(this).animate({fontSize: "+=6px"});
});
$("p").on("click", function(){
//this get execute multiple times
alert('test');
});
});
html
<p>Click any p element to increase its text size. The event will only trigger once for each p element.</p>

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

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.

jquery: how to add event handler to dynamically added element without adding it to existing element again

I'll try to explain my problem:
I have a website where the user dynamically adds elements. They all belong to the "toBuy" class. Whenever a new element is added to this class I need to attach a click-handler to only this element but not to all others. To keep my code clean I want to have a function that does this work. Here is what i've tried:
this is how the stuff is added:
$("#addItemButton").click(function(){
var item= $('#item').val();
$('#item').val("");
var quantity= $('#quantity').val();
$('#quantity').val("");
var comment=$('#addComment').val();
$('#addComment').val("");
//construct new html
var newitem="<div class='toBuyItem'><div class='item'>";
newitem+=item;
newitem+="</div><div class='quantity'>";
newitem+=quantity;
newitem+="</div><div class='comment'><img src='img/comment";
if(comment==""){
newitem+="_none"
}
newitem+=".png' alt='Comment'></div><div class='itemComment'>"
newitem+=comment;
newitem+="</div></div>";
$("#toBuyItems" ).prepend( newitem );
toggle("#addItemClicked");
initializeEventListeners();
});
then this is the initializeEventListeners function (which I also run when the page loads so that the existing elements have the event handlers already:
function initializeEventListeners(){
$(".toBuyItem").click(function(){
console.log($(this).html());
console.log($(this).has('.itemComment').length);
if($(this).has('.itemComment').length != 0){
console.log("toggling");
$(this).addClass("toggling");
toggle(".toggling .itemComment");
$(this).removeClass("toggling");
}
});
}
function toggle(item){
$( item ).slideToggle(500);
}
now apparently what happens is that when a new element is added the existing elements get a new event handler for clicking (so they have it twice). Meaning that they toggle on and off with just one click. Probably it's damn simple but I cannot wrap my head around it....
EDIT:
so this works:
$(document).on('click', '.toBuyItem', function(){
if($(this).has('.itemComment').length != 0){
console.log("toggling");
$(this).addClass("toggling");
toggle(".toggling .itemComment");
$(this).removeClass("toggling");
}
});
Use jquery's on method. This way you have to add event only once. This will be added automatically to dynamically added elements.
$(document/parentSelector).on('click', '.toBuyItem', function() {
// Event handler code here
});
If you are using parentSelector in the above syntax, it has to be present at the time of adding event.
Docs: https://api.jquery.com/on
You can use jQuery.on method. It can attach handlers to all existing in the DOM and created in future tags of the selector. Syntax is as follows:
$(document).on('click', '.toBuyItem', function(){
//do onClick stuff
})
As others have suggested, you can delegate click handling to document or some suitable container element, and that's probably what I would do.
But you could alternatively define a named click handler, which would be available to be attached to elements already present on page load, and (scope permitting) to elements added later.
You might choose to write ...
function buy() {
if($(this).has('.itemComment').length != 0) {
$(this).addClass("toggling");
toggle(".toggling .itemComment");
$(this).removeClass("toggling");
}
}
function initializeEventListeners() {
$(".toBuyItem").on('click', buy);
}
$("#addItemButton").on('click', function() {
var item = $('#item').val(),
quantity = $('#quantity').val(),
comment = $('#addComment').val();
$('#item', '#quantity', '#addComment').val("");
//construct and append a new item
var $newitem = $('<div class="toBuyItem"><div class="item">' + item + '</div><div class="quantity">' + quantity + '</div><div class="comment"><img alt="Comment"></div><div class="itemComment">' + comment + '</div></div>').prependTo("#toBuyItems").on('click', buy);// <<<<< here, you benefit from having named the click handler
$newitem.find(".comment img").attr('src', comment ? 'img/comment.png' : 'img/comment_none.png');
toggle("#addItemClicked");
});

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