forEach loop only works on the last item - javascript

I'm trying to add a button the multiple divs on a page. Why does it work only on the last item?
const columns = document.querySelectorAll('.hcolumn');
const button = document.createElement('div');
button.innerText = 'Expand text';
button.classList.add ('button');
columns.forEach( column => {
column.append(button)
});
Here's the codepen

That's because you're are appending the same button element each time which is a single reference so it gets removed from previous element and gets appended to next one. You also need to create a new button everytime like so :-
const columns = document.querySelectorAll('.hcolumn');
columns.forEach( column => {
const button = document.createElement('div');
button.innerText = 'Expand text';
button.classList.add ('button');
column.append(button)
});

Related

Why are copies of whats in my array posting?

i want the value from my input to render on my page when i click on my button to add text to my page. For some reason with the current code that i have, the new value from my input is displayed in addition to a copy of what was already there, so basically previous input values are showing more than once on the front end as opposed to a new item just being added to the list
heres a link to the codepen so you can see what happens https://codepen.io/matthew-angel/pen/jOmJLBW
// Log out "Button clicked!" when the user clicks the "SAVE INPUT" button
let myLeads = []
const button = document.querySelector("#input-btn")
const inputEl = document.querySelector("#input-el")
// let inputText = inputEl.value
const ulEl = document.querySelector("#ul-el")
button.addEventListener("click", addText)
function addText() {
console.log(inputEl.value)
myLeads.push(inputEl.value)
console.log(myLeads)
render()
}
function render(){
for(let i = 0; i < myLeads.length; i++) {
// create element
// set text content
// append to ul
let li = document.createElement('li')
li.innerText += myLeads[i]
ulEl.appendChild(li)
console.log(li)
console.log(ulEl)
console.log( li.textContent)
}
}

I want to use vanilla JS to remove the parent element of the button I'm pressing on click

I created a meme generator that accepts text and images and puts them all together. The generator creates a unique ID for each 'meme' and also adds buttons that show up on hover to "delete" the meme. I used vanilla JS to create this and therefore had to layer a few different child divs on top of one another: image, top text, bottom text using z-index.
I am struggling, however, to get the button to delete the parent div. I want to be able to click that delete button, and have the parent div deleted so that the button goes away, along with image and text. picture below + code snippets.
I attemped to do it with vanilla javascript, by adding a closeTheMeme function on click to each button:
let deleteBtns = document.getElementsByClassName('.delete');
function closeTheMeme (){
this.parentElement.parentElement.removeChild();
};
for(let i=0;i<deleteBtns.length;i++){
deleteBtns[i].addEventListener("click",closeTheMeme);
}
No errors on console...Including the rest of the JS below so you can see how the elements are created on click of the meme generator.
'use strict';
let count=0;
// SUBMIT FORM
document.getElementById('memeInput').addEventListener('submit',function(e){
count++;
//prevent default
e.preventDefault();
//set image, top, and bottom to variables we can work with
let bottomText = document.getElementById('bottomText').value;
createMeme();
})
function createMeme(){
//create a meme section with an ID of the number of times the button was clicked, and add it to the meme section
let meme = document.createElement("DIV");
document.body.appendChild(meme);
meme.setAttribute("id", "meme"+count);
//create an image, set that image to equal the link, give it an id based on form submits, set image.src equal to the link
let img = document.createElement("IMG");
img.setAttribute("id","image"+count);
let imageLink = document.getElementById('imageLink').value;
meme.appendChild(img);
document.getElementById("image"+count).src=imageLink;
//set top text variable equal to the ID of toptext. value(form submission)
let topText = document.getElementById('topText').value;
let top = document.createElement('DIV');
top.setAttribute("id","topText"+ count);
meme.appendChild(top);
top.innerHTML = topText;
//set bottom text variable equal to the ID of toptext.value form submission
let bottomText = document.getElementById('bottomText').value;
let bottom = document.createElement('DIV');
bottom.setAttribute("id","bottomText" + count);
meme.appendChild(bottom);
bottom.innerHTML = bottomText;
//add a button that deletes the meme in the same way as above
let deleteButton = document.createElement("BUTTON");
deleteButton.classList.add("delete");
deleteButton.innerHTML = "Delete";
meme.appendChild(deleteButton);
//styling and position
meme.classList.add("meme");
top.classList.add("topWords");
bottom.classList.add("bottomWords");
};
let deleteBtns = document.getElementsByClassName('.delete');
function closeTheMeme (){
this.parentElement.parentElement.removeChild();
};
for(let i=0;i<deleteBtns.length;i++){
deleteBtns[i].addEventListener("click",closeTheMeme);
}
You select the buttons when the page loads. You created no buttons so it is not possible for it to work since there is nothing to bind an event to.
Since you are making the buttons, add the event there.
const deleteButton = document.createElement("BUTTON");
deleteButton.addEventListener("click", function () {
this.closest("div").remove();
});
Other option is event delegation
document.body.addEventListener("click", function (e) {
const btn = e.target.closest(".delete");
if (btn) btn.closest("div").remove();
});

Create multiple elements and delete a single one JavaScript

I'm working on a JavaScript project where a user can click a button to create a text element. However, I also want a feature where I can click a different button and the element that was created most recently will be removed, so In other words, I want to be able to click a button to create an element and click a different button to undo that action.
The problem I was having was that I created the element, then I would remove the element using:
element.parentNode.removeChild(element); , but it would clear all of the elements that were created under the same variable.
var elem = document.createElement("div");
elem.innerText = "Text";
document.body.appendChild(elem);
This code allows an element to be created with a button click. All elemente that would be created are under the "elem" variable. so when I remove the element "elem", all element are cleared.
Is there a simple way to remove on element at a time that were all created procedurally?
Thanks for any help
When you create the elements, give the a class. When you want to remove an element, just get the last element by the className and remove it.
The below snippet demonstrates it -
for(let i = 0; i<5; i++){
var elem = document.createElement("div");
elem.innerText = "Text " + i;
elem.className = "added";
document.body.appendChild(elem);
}
setTimeout(function(){
var allDivs = document.getElementsByClassName("added");
var lastDiv = allDivs.length-1;
document.body.removeChild(allDivs[lastDiv]);
}, 3000);
I would probably use querySelectors to grab the last element:
// optional
// this is not needed it's just a random string added as
// content so we can see that the last one is removed
function uid() {
return Math.random().toString(36).slice(2);
}
document.querySelector('#add')
.addEventListener('click', (e) => {
const elem = document.createElement('div');
elem.textContent = `Text #${uid()}`;
document.querySelector('#container').appendChild(elem);
// optional - if there are elements to remove,
// enable the undo button
document.querySelector('#undo').removeAttribute('disabled');
});
document.querySelector('#undo')
.addEventListener('click', (e) => {
// grab the last child and remove
document.querySelector('#container > div:last-child').remove();
// optional - if there are no more divs we disable the undo button
if (document.querySelectorAll('#container > div').length === 0) {
document.querySelector('#undo').setAttribute('disabled', '');
}
});
<button id="add">Add</button>
<button id="undo" disabled>Undo</button>
<div id="container"></div>

Where & How to add counter and use it to increment innerText of appended list items - DOM

Using DOM - The button will append items to a list when clicked, but it also needs to increment the innerText integer [i] of each appended list item. I don't know how to make it increment.
I've tried adding a counter inside function insertItem(), and also inside button.addEventListener without success. Example of counter:
let clickCount = 0;
clickCount++:
HTML
<body>
<button class='button'>Click to add new list item</button>
</body>
JavaScript
let button = document.querySelector('button');
document.body.appendChild(button);
button.className = 'button';
button.addEventListener('click', insertItem);
function insertItem() {
let list = document.createElement('ul');
document.body.appendChild(list);
for (let i = 1; i === 1; i++) {
let item = document.createElement('li');
let text = document.createTextNode('This is list item ' + [i]);
item.appendChild(text);
list.appendChild(item);
document.getElementsByName('item').innerHTML = 'This is list item ' + [i+1];
}
}
I want innerText ('This is list item ' + [i]) to increment by 1 with each button click.
As of now, my code functions but the [i] doesn't increment; it remains as '1' with every click/append.
You have a few issues:
Your button is already part of your HTML - there is no need to append it again at the start.
You use a for loop, but it doesn't loop and instead only runs your code once (thus, there is no need for it)
You are creating multiple unordered lists (ul tags), when you can instead create one outside of your function, and add items (li tags) into that ul tag.
Once you have resolved these issues, you can create a global variable called clickCounter and use that as the number at the end of each of your items. Each time you add an item to your list you can increment clickCounter using clickCounter++. I have made clickCounter global as if it was inside your function it will reset every time your button is clicked which is not what you want.
See working example below:
let button = document.querySelector('button.button'); // get the button element with the button class
button.addEventListener('click', insertItem);
let clickCounter = 1;
let list = document.createElement('ul');
document.body.appendChild(list);
function insertItem() {
let item = document.createElement('li');
let text = document.createTextNode('This is list item ' + clickCounter);
item.appendChild(text);
list.appendChild(item);
clickCounter++;
}
<button class='button'>Click to add new list item</button>

Event listener doesn't work after adding a new element

Basically, I have a text box where I write some text, click "Add Post", and a new post will be prepended to a list of other posts (similar to Twitter). Each of these posts has a 'delete' button.
For now, when I click delete on each posts, I have a console.log that just says, "TEST". And it works fine. However, after I add a post, meaning, a new element has been prepended, the event listener stops working, even for the existing 'delete' buttons (not just the newly added one).
This is how I wrote the event listener for the delete.
for(var i = 0; i < deleteDOM.length; i++) {
deleteDOM[i].addEventListener("click", deleteEntryCont);
}
...Where deleteEntryCont is just a function that console logs "TEST" for now.
deleteDOM is the variable for document.getElementsByClassName("delete") and is just a node list.
Here's the part where a new 'post' is added:
entryList.forEach(function(entry) {
var entryItemDOM = document.createElement("li");
var entryTextDOM = document.createElement("p");
var metaWrapperDOM = document.createElement("div");
var timeStampDOM = document.createElement("span");
var deleteDOM = document.createElement("span");
// Create entry wrapper & class names
entryItemDOM.className = "entry";
entryItemDOM.className += ` ${entry.mood}-entry`;
entryItemDOM.id = entry.id;
// Insert entry at the top of the stack
domEl.entriesDOM.insertBefore(entryItemDOM, domEl.entriesDOM.firstChild);
entryItemDOM.appendChild(entryTextDOM);
entryTextDOM.innerHTML = entry.text;
entryItemDOM.appendChild(metaWrapperDOM);
metaWrapperDOM.className = "overflow-hidden";
metaWrapperDOM.appendChild(timeStampDOM);
timeStampDOM.className = "timestamp";
timeStampDOM.innerHTML = entry.timeStamp;
metaWrapperDOM.appendChild(deleteDOM);
deleteDOM.className = "delete";
deleteDOM.innerHTML = "Delete";
});
Where entryList is an array of objects that this above code renders in HTML.
What could be the issue?
As discussed in the comments, you're only adding the event listeners when the page is loaded, which means it's only added to the posts that are visible at the time. You need to add the listener separately to each new post you create:
metaWrapperDOM.appendChild(deleteDOM);
deleteDOM.className = "delete";
deleteDOM.innerHTML = "Delete";
deleteDOM.addEventListener('click', deleteEntryCont);

Categories