I swear I scoured the forum for a solution, but I honestly just started learning Javascript a few days ago so I am not very good at deducing solutions from different code and using the logic on my own. Total noob I am.
I want my delete button to delete the item it is assigned to when clicked. I do not understand why my deleteMe() function doesn't do anything.
var button = document.getElementById("enter");
var input = document.getElementById("userinput");
var ul = document.querySelector("ul");
var listItem = document.querySelectorAll("li");
var dlt = document.getElementsByClassName(".delete")
function deleteMe() {
dlt.addEventListener("click", function() {
this.removeChild();
})
}
function delButton(parent) {
var del = document.createElement("button");
del.appendChild(document.createTextNode("Done!"));
parent.appendChild(del);
button.className = "delete";
}
function addToggle() {
this.classList.toggle("done");
}
function addEntry() {
if (input.value.length > 0 ) {
var newItem = document.createElement("li");
newItem.appendChild(document.createTextNode(input.value));
ul.appendChild(newItem);
input.value="";
newItem.addEventListener("click",addToggle);
delButton(newItem);
}
}
for ( var i = 0 ; i < listItem.length; i++) {
listItem[i].addEventListener("click", addToggle);
delButton( listItem[i]);
}
button.addEventListener("click", addEntry);
input.addEventListener("keypress", function() {
if ( event.keyCode === 13 ) {
addEntry()}
});
I would really really appreciate if someone could explain what I am doing wrong.
Thank you!
dlt is currently an HTMLCollection, not an element, so you can't add an event listener to it. Either select an element from it first (such as through iteration), or even better, assign the listener to the container, and have the listener check to see if the clicked target's className is delete. I would add the listener to the ul, just once, near the beginning of your script:
ul.addEventListener('click', (e) => {
// e.target represents the clicked element
if (e.target.className !== 'delete') return;
e.target.parentElement.remove();
});
If you wanted to assign a separate listener to each delete, you would have to add the listener inside delButton, like so:
function delButton(parent) {
var del = parent.appendChild(document.createElement("button"));
del.textContent = 'Done!';
button.className = "delete";
button.addEventListener('click', () => button.parentElement.remove());
}
Related
Think of the Uno Card Game logic for example, how could I get the clicked Card on the players hand, when I create the cards on the players hand dynamically by clicking on a draw button?
something like:
var btn = document.createElement("BUTTON");
btn.onclick = () => {
alert(btnCounter);
}
btnCounter++;
but the onclick function should be written once and then be saved for this button.
One option is to rely on event bubbling. We can add the event listener to the container for our hand and then, on click, make sure the clicked element was a button. In this example, I put our card's value in the button's dataset, but you can grab the info from anywhere on the target.
const hand = document.querySelector("#hand");
hand.addEventListener("click", function(e) {
if (e.target.tagName !== "BUTTON") return;
console.log(e.target.dataset.card);
});
function drawCards(numberOfCards) {
for (let i = 0; i < numberOfCards; i++) {
const button = document.createElement("button");
const card = Math.floor(Math.random() * 10);
button.setAttribute("data-card", card);
button.innerHTML = card;
hand.appendChild(button);
}
};
drawCards(5);
<div id="hand"></div>
In the context where the button (and its id) are created, bind the id in the o'clock function...
const ids = ['1', '2', '3'];
var parent = document.getElementById('parent')
ids.map(id => {
let btn = document.createElement('button');
btn.setAttribute('id', id)
btn.innerHTML = `Button ${id}`
parent.appendChild(btn)
btn.onclick = event => {
alert(id)
}
})
<div id="parent">
</div>
I am trying to understand the basics of eventListeners, i have created a simple form where i just want to add a value of an input to a UL, however when i append the value, i am able to see it in the list for a brief second and then it is instantly removed, i cannot figure out why, could anyone help?.
const submitButton = document.querySelector('#add-task-btn');
const clearButton = document.querySelector('#remove-task-btn');
const item = document.querySelector('#task');
const taskList = document.querySelector('.collection');
allEventListeners();
function allEventListeners(){
submitButton.addEventListener('click', function(){
if (item.value === ''){
alert('Please add a task')
};
const li = document.createElement('li');
li.appendChild(document.createTextNode(item.value));
taskList.appendChild(li);
item.value = "";
})
}
You just need to provide an event parameter for your handler function and then call preventDefault()
submitButton.addEventListener('click', function(ev){
ev.preventDefault(); // prevent the page submit
//...
});
I have a "+" button that , when clicked, triggers the creation of a block with an input and 2 buttons, one for validating the input and one for removing it.
// My code looks almost like this :
addBtn.addEventListener('click', e => {
addClick++;
// All the elements of the same line (input and 2 buttons) have an int in common in their id string ==> addClick
// I'm missing all the declarations of the variables here
blockDiv.appendChild(posteInput);
blockDiv.appendChild(validateBtn);
blockDiv.appendChild(deleteBtn);
globalPostesBlock.appendChild(blockDiv)
let allDeleteBtn = document.getElementsByClassName('delete-button');
for (let i = 0; i < allDeleteBtn.length; i++) {
allDeleteBtn[i].addEventListener('click', e => {
// Retrieving the block with the same id
let deleteBtnId = parseInt((allDeleteBtn[i].getAttribute('id').match(/\d+/g)).toString());
let singlePosteBlock = document.getElementById(`poste-block-${deleteBtnId}`);
singlePosteBlock.remove();
}
})
}
The event listener represents the action of clicking the delete button so it can remove its entire containing block
I have the same logic for the validate button, but I'm using ajax in it.
Each time I create a new block, I want to create an event listener associated to this block, but all I found so far is an event listener with a loop on every buttons, so what happens is that the action triggers as many time as the block numbers because of the loop, but I don't know how to dissociate every event listeners.
If I have 3 blocks and I validate one input value which is being inserted in the DB afterwards, the value is being inserted 3 times.
Does this help ?
//id pool
let latestId = 0;
//retrive the button
var myButton = document.querySelector("button");
myButton.addEventListener("click", createKids);
//function declaration :: createKids
function createKids() {
latestId++;
//declare and initialization
let div = document.createElement("div");
let input = document.createElement("input");
let buttonSend = document.createElement("button");
let buttonDelete = document.createElement("button");
//append input & buttons to div
div.appendChild(input);
div.appendChild(buttonSend);
div.appendChild(buttonDelete);
//Some beautifying
buttonSend.innerText = "Send me";
buttonDelete.innerText = "Delete me";
//some logic
div.dataset.id = latestId;
//event handeling
buttonSend.addEventListener("click", sendItem);
buttonDelete.addEventListener("click", deleteItem);
//insert div
document.body.appendChild(div);
}
function sendItem(event) {
//do action and delete ?
let input = event.target.parentNode.querySelector("input");
//retrive data
let val = input.value;
let id = event.target.parentNode.dataset.id;
//disable input for fun ^^
input.disabled = true;
//console istead of send
console.log(id,val);
//handle some more
setTimeout(() => {
event.target.parentNode.remove();
}, 3000);
}
function deleteItem(event) {
event.currentTarget.parentNode.remove();
}
<p>Does this help?</p>
<button>Magic Button</button>
I am having some trouble understanding what is happening in a piece of vanilla JS for the Isotope filter. The original code is here: https://codepen.io/desandro/pen/VWLJEb
var buttonGroups = document.querySelectorAll('.button-group');
for (var i = 0; i < buttonGroups.length; i++) {
var buttonGroup = buttonGroups[i];
var onButtonGroupClick = getOnButtonGroupClick(buttonGroup);
buttonGroup.addEventListener('click', onButtonGroupClick);
}
function getOnButtonGroupClick(buttonGroup) {
return function(event) {
// check for only button clicks
var isButton = event.target.classList.contains('button');
if (!isButton) {
return;
}
var checkedButton = buttonGroup.querySelector('.is-checked');
checkedButton.classList.remove('is-checked')
event.target.classList.add('is-checked');
}
}
What is happening between the getOnButtonGroupClick function and it being assigned to a variable in the for loop preceding it?
getButtonGroupClick returns a closure that saves the value of buttonGroup. When you click on a button in the button group, it uses that closure variable to search for the checked button in the group, uncheck it, and then check the button you clicked on.
This complexity isn't really needed. When an event listener is called, event.currentTarget is set to the element that the listener was attached to, so you could just use that.
var buttonGroups = document.querySelectorAll('.button-group');
for (var i = 0; i < buttonGroups.length; i++) {
var buttonGroup = buttonGroups[i];
buttonGroup.addEventListener('click', onButtonGroupClick);
}
function OnButtonGroupClick(event) {
// check for only button clicks
var isButton = event.target.classList.contains('button');
if (!isButton) {
return;
}
var checkedButton = event.currentTarget.querySelector('.is-checked');
checkedButton.classList.remove('is-checked')
event.target.classList.add('is-checked');
}
The for loop is used to iterate over all of the elements with the class of button-group and and a click event listener to them. getOnButtonGroupClick returns a function to be used as the function to be used as the event listener for the element i.e. the function that is run when the element is clicked on.
var buttonGroups = document.querySelectorAll('.button-group');
//get all elements within the document with a class of button-group
//buttonGroups is a NodeList
for (var i = 0; i < buttonGroups.length; i++) {
//loop through all of the elements with a class of button-group matched by the above query selector
var buttonGroup = buttonGroups[i];
//get the element in the NodeList with the index i
var onButtonGroupClick = getOnButtonGroupClick(buttonGroup);
//get the function to be run when the element is clicked on
buttonGroup.addEventListener('click', onButtonGroupClick);
//add a click event listener to the element
}
function getOnButtonGroupClick(buttonGroup) {
return function(event) {
// check for only button clicks
var isButton = event.target.classList.contains('button');
//check if the element has a class of button
if (!isButton) {
//if the element does not have a class of button, do nothing
return;
}
var checkedButton = buttonGroup.querySelector('.is-checked');
checkedButton.classList.remove('is-checked')
event.target.classList.add('is-checked');
}
}
If I understood your question correctly, It means that a click event is being added to every button in the buttonGroups there is. Although, if you ask me, it would be way better and cleaner to just use a forEach, like so:
const buttonGroups = document.querySelectorAll('.button-group');
buttonGroups.forEach(button => button.addEventListener("click", OnButtonGroupClick)
function OnButtonGroupClick(event) {
// check for only button clicks
let isButton = event.target.classList.contains('button');
if (!isButton) {
return;
}
let checkedButton = event.currentTarget.querySelector('.is-checked');
checkedButton.classList.remove('is-checked')
event.target.classList.add('is-checked');
}
So, you add a click event to ALL the buttons in the buttonGroups that will run the function onButtonGroupClick.
EDIT: And there's no really need to assign the function like that... at all. Just call it on the click event and that's it.
I need to first remove the event listener before dynamically adding more elements which also need the same event listener. I am using an external function name (not an anonymous function) and specifying the same useCapture value in both the add and remove.
The function is nested within another function. < suspected problem was the problem
You can see the problem by clicking the first "add button" more than once. The first click adds one more button, the second click adds two more, the third click adds four more, etc. Each click should only add one more. I guess the return value of removeEventListener is always undefined so I can only tell that removal did not work from the duplicate events.
var app = function() {
console.log('app');
var setup = function() {
console.log('setup');
var addButton = function(e) {
console.log(e);
var button = e.target;
var newButton = document.createElement('BUTTON');
newButton.innerText = 'add another button';
button.parentNode.appendChild( newButton );
setup();
}
var buttons = document.querySelectorAll('button');
for(var i=0; i<buttons.length; i++) {
var button = buttons[i];
button.removeEventListener('mousedown', addButton, false);
button.addEventListener('mousedown', addButton, false);
}
}
setup();
}
app();
<div>
<button>add button</button>
</div>
Removing the addButton function from the nest fixed the problem.
Also, defining app.addButton within app.setup or within the nest causes app.addButton to be overwritten each time app.setup is called which destroys the reference needed for removeEventListener. So the name alone is not all that matters. It must be the original function, not an exact copy.
var app = {};
app.addButton = function(e) {
console.log(e);
var button = e.target;
var newButton = document.createElement('BUTTON');
newButton.innerText = 'add another button';
button.parentNode.appendChild( newButton );
app.setup();
}
app.setup = function() {
console.log('setup');
var buttons = document.querySelectorAll('button');
for(var i=0; i<buttons.length; i++) {
var button = buttons[i];
button.removeEventListener('mousedown', app.addButton, false);
button.addEventListener('mousedown', app.addButton, false);
}
}
app.init = function() {
console.log('app');
app.setup();
}
app.init();
<div>
<button>add button</button>
</div>