onclick inside forEach, value parameter only refers to last element - javascript

I have a list of anchor in my html, I want to make their href editable.
Everything fine, but the validation step (last onclick) refers to the last anchor instead of the current one
var anchors = document.querySelectorAll('.home-content a');
var col = document.querySelectorAll('.home-content > article');
anchors.forEach((k)=> {
let linkpanel = document.getElementById('link-edit-panel'); //This element is a single div in my html
let linkpanelvalidate = document.getElementById('validate-link'); //the button inside the said div
let editinput = linkpanel.querySelector('input'); //the input inside this div
//For each anchors, I add a button that will let user show the "linkpanel" div to edit the href of this anchor
let editbut = document.createElement('div');
let linktxt = k.href;
editbut.classList.add('edit-but','toremove');
editbut.innerHTML = "<i class='fas fa-link'></i>";
//I put this new element to the current anchor
k.appendChild(editbut);
console.log(k); // this to show me the full list of anchors
/* PROBLEM START HERE */
//click on the "edit" button
editbut.onclick = ()=>{
console.log(k); //Here, it shows the good anchor!
}
//click on the "validate" button
linkpanelvalidate.onclick = ()=>{
console.log(k); //Here, it shows the very last anchor...
}
});
I tried to put the element inside a constant
const ttt = k;
It does not change a thing.
Thank you for your help

We are facing here a classical forEach bubbling misunderstand (and I was blind not to see it)
When the click on the validate button occures, the call is made from the "main" bubble (outside the loop function if you need to picture it) so naturaly, it returns the last occurrence of the loop when we print the value in the console for example.
Solution
There is many solutions, you can store these values in an array to use each of them later
var arr = [];
node.forEach((v)=>{
arr.push(v);
});
Or, you don't want to deal with an array and want to keep it simple, like me, and you create your button during the forEach loop event, like this
node.forEach((v)=>{
let btn = document.createElement('button');
document.body.appendChild(btn);
btn.onclick = ()=> {
console.log(v); //it is the current value, not the last one
//you can create another button here and put his onclick here, the value will still remains etc
}
});

Related

Some of Icon click listeners work and some not in a strange way in JavaScript

I am working on a to-do list application. I create a li item and put 2 icons and a p tag in it. One of the icons is edit and it works quite well, I replace an input with the p tag and it is fine but the problem is that my check icons on the left side work half way. If I add the li items one by one, the check icons work very well but when I add 5 or 10 items and then try to check the icons, a few of them works and the others do not. I have tried replacing i tags with span tags and no result. It is like every second li tag blocks the former one. I need help, I would appreciate any.
I'll add below the only the icons which don't work.
const DONE = document.getElementsByClassName('far fa-circle');
const LINE = document.getElementsByClassName('list-points');
const EDIT = document.getElementsByClassName('far fa-edit');
const CONTAINER = document.getElementById("actual-container");
const BUTTON = document.getElementById("list-adder");
BUTTON.addEventListener('click', nameList);
function nameList() {
const item1 = document.createElement("i");
item1.className = "far fa-circle";
const paraph1 = document.createElement("p");
paraph1.className = "list-points";
paraph1.innerText = "Fresh again!";
const item2 = document.createElement("i");
item2.className = "far fa-edit";
const myList = document.createElement("li");
myList.appendChild(item1);
myList.appendChild(paraph1);
myList.appendChild(item2);
CONTAINER.appendChild(myList);
for (let i = 0; i < DONE.length; i++) {
DONE[i].addEventListener('click', function() {
DONE[i].classList.toggle('fa-times-circle');
})
}
}
<head>
<title>Debug</title>
<script src="https://kit.fontawesome.com/ae444f90db.js" crossorigin="anonymous"></script>
</head>
<body>
<div>
<ul id="actual-container"></ul>
</div>
<button id="list-adder">ME</button>
</body>
The error is within the assignment of your click-handlers.
You do
for (let i = 0; i < DONE.length; i++) {
DONE[i].addEventListener('click', function() {
DONE[i].classList.toggle('fa-times-circle');
});
}
This will add a toggling event handler to all elements that you keep in DONE and has this behaviour.
Click button: create elements A, assign click handler to A
(A has a single handler) works
Click button: create elements B, assign click handler to A and B
(A has two handlers) does not work
(B has a single handler) works
Click button: create elements C, assign click handler to A and B and C
(A has three handlers) works
(B has two handlers) does not work
(C has a single handler) works
Because you are using toggle in your click handler, the handlers are "canceling" each other because toggling something twice will leave you in the initial state.
I guess you are not aware of the fact that getElementByClassName returns a live list, so that the elements of your variable DONE are changed when you add new elements to the DOM. I was not aware of this either, so thank you for your question :)
See here for a better explanation: https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName
In your code it should be enough to add the handler just to the one element you create (item1 is the icon element in your code):
item1.addEventListener('click', function() {
item1.classList.toggle('fa-times-circle');
});

innerHTML of button not logging properly on click event initialized in for loop

Right now, I have an array of names, I'm assigning each name to a button and I'm setting the button's inner html to the name
var FButton = document.createElement('button')
FButton.innerHTML = Name
document.getElementById('FBContainer').appendChild(FButton)
I am then looping through these "FButtons" then assigning a click event like so:
function AttachListener(){
const buttons = document.querySelectorAll("#ChatButton")
for(var button of buttons){
button.addEventListener('click', function(){
ChatContainer.style.visibility = "visible";
while(ChatContent.firstChild){
ChatContent.removeChild(ChatContent.lastChild)
}
console.log(button.innerHTML)
})
}
}
Now, whenever i console log the button's innerHTML, it always returns the innerHTML of the last 'button' in the 'buttons' array. What might be causing this to happen?

Dom Traversing the Dom with JavaScript but event not hitting the Html Element

I want to add delete functionality to a button on an Li element that is dynamically created by Javascript but I can't seem to be able to get the event listener to hit the target button.
I tried adding it right away by doing
var trashButton = document.getElementsByClassName("deleteListItemButton");
trashButton.addEventListener('click',removeLiItem);
but I got an unhandled exception error
var idForLiElement =1;
document.getElementById("addTaskId").addEventListener('click', function(){
var valueFromTextBox = document.getElementById("textBoxId").value;
if(valueFromTextBox) addItemToDo(valueFromTextBox);
});
function addItemToDo(valueFromTextBox){
var unorderedList = document.getElementById("toDoId")
var listElement = document.createElement("li");
listElement.className ="listItem";
listElement.innerHTML = valueFromTextBox;
listElement.id =Number(idForLiElement);
idForLiElement ++;
//puts the newest list element before the last element
unorderedList.insertBefore(listElement, unorderedList.childNodes[0]);
//creates the div that will contain both buttons in each list element
var buttonsContainer = document.createElement("div");
buttonsContainer.className ="listItemButtonContainer";
//creates the delete button and assigns it a class name
var deleteButton = document.createElement("Button")
deleteButton.className ="deleteListItemButton";
//creates the complete button and assigns it a class name
var completeButton = document.createElement("Button")
completeButton.className ="completeListItemButton";
//creates the delete image tag and assigns it a class name
var trashImageTag = document.createElement("i")
trashImageTag.className = "fa fa-trash fa-2x";
//creates the check mark button and assigns it a class name
var checkMarkImageTag = document.createElement("i")
checkMarkImageTag.className= "fa fa-check fa-2x";
//appends delete image tag to delete button
deleteButton.appendChild(trashImageTag);
//appends check mark image tag to complete button
completeButton.appendChild(checkMarkImageTag);
//appends delete button to button container
buttonsContainer.appendChild(deleteButton);
//appends complete button to button container
buttonsContainer.appendChild(completeButton)
//appends button container to list element
listElement.appendChild(buttonsContainer);
};
var trashButton = document.getElementsByClassName("deleteListItemButton");
for (i = 0; i < trashButton.length; i++) {
trashButton[i].addEventListener('click',removeLiItem)
};
function removeLiItem(e){
console.log(this)
};
I just want the event listener to hit console.log so I know the button is working
Attach the event to the element you've just created
deleteButton.addEventListener('click', function() { console.log('hit!') })
I want to add delete functionality to a button on an Li element that is dynamically created by javascript but I cant seem to be able to get the event listener to hit the target button.
The elements are dynamically created, so by the time the elements are created, the for loop where you add the event listeners has already executed.
This is what event propogation is for. Let the parent element (ul) listen to event clicks instead of having each child hold the responsibility. That way it doesnt matter how many children elements are added, momma (parent element) will always be listening for clicks.
You can do something like this:
// on your ul element
const itemList = document.querySelector(".item-list");
// listen for click on here
itemList.addEventListener('click', removeLiItem);
// you can access the actual element through the event's `target`
function removeLiItem (event) {
if (event.target.classList.contains('deleteListItemButton') {
// remove element
}
}

How do I swap cells using two Onclick Events in Javascript

I am trying to swap two cells using two separate click events in javascript. The problem is that the values stored by the first click event is overwritten by the second click event, and the console shows me that the StringAdjacent value stored for the second click event has been overwritten. This is my code:
//Listen to a Set of Click Events to Swap Cells
document.getElementById('board').addEventListener("click", function(e){
click1ID = event.target.id;
click1Class = event.target.className;
stringAdjacency1 = click1ID.replace('cell','')
console.log(stringAdjacency1);
document.getElementById('board').addEventListener("click", function(e){
click2ID = event.target.id;
click2Class = event.target.className;
stringAdjacency2 = click2ID.replace('cell','')
console.log(stringAdjacency2);
});
console.log(stringAdjacency1, stringAdjacency2);
});
function swapIds(click1ID, click1Class, click2ID, click2Class) {
//Are cells adjacent? If so, swap Cells
//Check the winning combinations to see if there's a new match;
//Swap cells;
});
Please help! Thank you.
You are adding a click event listener, which when fired by clicking the first cell causes another click event listener to be added. So when you click the second cell, it will fire the first event listener (overwriting the value), plus the second listener you added after the first one.
You only need to register a single listener who's function can handle the logic:
Something like this should work (didn't test):
var firstCell = null;
document.getElementById('board').addEventListener("click", function(e){
if(!firstCell) {
firstCell = e.target;
} else {
var secondCell = e.target;
// do whatever logic you want
// reset first cell
firstCell = null;
}
});
This will set firstCell to be the first cell clicked, then the second click it will no longer be null, so it will go into the else condition and you can do whatever you want. Then you'll reset firstCell so the entire interaction can be repeated.

DOM: Delete newly created 'article' element with newly created delete button within onclick event?

I have one section element that contains one article element. Also, I have one input button with 'onclick' event. Whenever this event fired, a new article element appended to the section element with unique id.
The newArticle element contains a label, text box and a delete button. All these three elements get created within the on-click event.
document.getElementById("addRow").onclick = function () {
var newCustomerlbl = document.createElement("label");
newCustomerlbl.innerHTML = "Cutomer Name: ";
var newCustomertxt = document.createElement("input");
newCustomertxt.setAttribute("type", "text");
var delBtn = document.createElement("input");
delBtn.setAttribute("type", "button");
delBtn.setAttribute("value", "Delete");
delBtn.setAttribute("id", "btnDelete");
var newArticle = document.createElement("article");
newArticle.appendChild(newCustomerlbl);
newArticle.appendChild(newCustomertxt);
newArticle.appendChild(delBtn);
var customerSection = document.getElementById("customerRecords");
var customerArticles = customerSection.getElementsByTagName("article");
for (var i = 0; i < customerArticles.length; i++) {
var lastDigit = i + 1;
var newArticleValue = "article" + lastDigit;
newArticle.setAttribute("id", newArticleValue);
}
customerSection.appendChild(newArticle);
}
Now what I want is whenever user click upon the newly created appended delete button, only that particular article get deleted without effecting the rest of articles.
Here is the my jsFiddle code.
If you don't want to use jQuery you can add event listeners to your buttons:
delBtn.addEventListener('click', function () {
this.parentElement.remove();
}, false);
https://jsfiddle.net/3nq1v5e1/
You need to bind an event listener on the newly created delete button. Your example code about using $(this) suggest that you are using JQuery, but then again in the rest of the code you are not using any JQuery?
If you are using JQuery, things get real simple, just add something like
$(document).on('click','.btnDelete', function(){
$(this).closest('article').remove();
});
(and remember to give the deletebutton a CLASS rather than ID, as there will be multiple delete buttons).
If you are NOT using JQuery, you need to add the event listener EVERY TIME a new delete button is created
newArticle.appendChild(delBtn);
delBtn.onclick = function(.....
etc.

Categories

Resources