vanilla js onclick remove parent element - javascript

So I have a bunch of divs created dynamically and I have to remove the parent elements of the ones I clicked the remove button on. Here is my code:
function add() {
let newBook = new addBookToLibrary(`"${author.value}"`, `${title.value}`, `${pages.value} pages`);
// create a div when clicked submit
// and give it class 'card', append to main-container
card = document.createElement('div');
main.appendChild(card);
card.classList.add('card');
cards = document.querySelectorAll('.card');
// append book info to card container
for (let i = 0; i < Object.keys(newBook).length; i++) {
div = document.createElement('div');
card.appendChild(div);
cardDiv = document.querySelectorAll('.card:last-child div');
cardDiv[i].textContent = Object.values(newBook)[i];
}
// create a remove button
remove = document.createElement('div');
remove.textContent = 'Remove';
remove.classList.add('remove');
card.appendChild(remove);
//event listener
remove.addEventListener('click', () => {
cards.forEach(card => card.remove());
})
};
submit.addEventListener('click', add);
This removes all cards when I click remove button. And when I try this approach:
remove.addEventListener('click', () => {
cards.forEach(card => this.card.remove());
It will only remove the last card div. Please help me fix this one.

have you tried this?
remove.addEventListener('click', (event) => {
event.target.parentElement.remove();
}

Inside the remove function: this.closest("div").remove();.

Related

forEach loop only works on the last item

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)
});

How Dynamically Created Button Target Individual Element

I am under the impression if we target an element by element name of class name it will be applicable to all elements or elements with that class name. But in the following code when I clicked a button it will only change the innerHTML of the nearest span element even though I have add event listener by selecting just button element. Please clarify. Thanks.
const myArray = [0, 1, 2];
let myContainer = document.querySelector('.container');
const newArray = myArray.map((item) => {
let newArticle = document.createElement('article');
const myHTML = `<article>
<span></span>
<button>Click ${item}</button>
</article>
`;
newArticle.innerHTML = myHTML;
let myBtn = newArticle.querySelector('button');
myBtn.addEventListener('click', (e) => {
newArticle.querySelector('span').innerHTML = 'clicked';
})
return newArticle;
});
newArray.forEach((item) => {
myContainer.appendChild(item);
});
<div class="container">
<section>
</section>
</div>
let myBtn = newArticle.querySelector('button');
returns the FIRST button in newArticle
let myBtn = document.querySelectorAll('button');
returns ALL buttons in document
You can tell us what you expect by clicking on each one button?

How to use a forEach loop on dynamically created elements

I am looking to add draggable functionality to my todo list project,
I am trying to use a forEach loop to loop over my elements and for now just change the opacity of each div when dragged. However I can not find a way for this to work on elements which I have created, the draggable attribute is added but they dont work with the loop.
function addTodo() {
const todoDiv = document.createElement('div')
todoDiv.classList.add('todo')
const newButton = document.createElement('button');
newButton.classList.add('todo-button')
const newLine = document.createElement('div')
newLine.classList.add('line')
const newTodo = document.createElement('li');
newTodo.innerText = todoInput.value
newTodo.classList.add('todo-text')
const newDelete = document.createElement('button');
newDelete.innerHTML = '<i class="fas fa-times "></i>'
newDelete.classList.add('delete-todo')
todoList.appendChild(todoDiv)
todoDiv.appendChild(newButton)
todoDiv.appendChild(newLine)
newLine.appendChild(newTodo)
newLine.appendChild(newDelete)
var att = document.createAttribute("draggable");
att.value = "true";
todoDiv.setAttributeNode(att);
}
todo.forEach(todo => {
todo.addEventListener('dragstart', () => {
todo.classList.add('dragging')
})
})
TLDR
Here is a fiddle, when you create a new todo item it doesnt seem to be picked up by the forEach loop and apply the class
Any help would be much appreciated!

Moving a Checked Li to the Bottom of a UL

I am making a todo list, i have just about everything for it figured out but one area i am stuck on is my UL and Li.
Basically when you enter items into the list, you have the ability to click the checkbox beside said item when you complete the task, and it will put a line through the text.
But i also want it to move that item to the bottom of the list when it is clicked.
would anyone be able to help me with how i would go about doing that
code Below
// making event listener for adding item
let addBTN = document.getElementById('addBtn');
addBTN.addEventListener('click', addItem);
// this creates a new li based on the entered value in the text box that it gets when you hit the button
// Through Research found that setAttribute isn't really needed and i can just use .id , .type etc
function addItem() {
// Creating needed elements as well as getting text from textbox
let newLi = document.createElement("li");
let myLiValue = document.getElementById('textBoxAdd').value;
let liTextNode = document.createElement("label");
liTextNode.textContent = myLiValue;
// makes div for li
let newDivID = ('div_' + myLiValue);
let newDiv = document.createElement('div');
newDiv.id = newDivID;
// makes checkboxes for the li
let newCheckBoxID = ('checkbox_' + myLiValue);
let newCheckBox = document.createElement('input');
newCheckBox.type = 'checkbox';
newCheckBox.id = newCheckBoxID;
// makes delete button for the li
let newDeleteID = ('deleteButton_' + myLiValue);
let newDeleteButton = document.createElement("button")
newDeleteButton.type = 'button';
newDeleteButton.id = newDeleteID
newDeleteButton.textContent = 'Delete';
//newDeleteButton.setAttribute('onclick', 'deleteItem()');
newDeleteButton.innerHTML = 'Delete';
// appends it to my newDiv
newDiv.appendChild(newCheckBox);
newDiv.appendChild(liTextNode);
newDiv.appendChild(newDeleteButton);
// then appends my new div to the new Li
newLi.appendChild(newDiv);
// this just makes sure a user cant enter in a blank value
if (myLiValue == "") {
alert("Please Enter Something Before Hitting Add Item");
} else {
document.getElementById('theNewList').appendChild(newLi);
document.getElementById('textBoxAdd').value = "";
}
}
//creating event listener for checkbox line through text and moving item
let theList = document.getElementById('theNewList');
theList.addEventListener('click', checkedComplete);
// function that will target every check box in the list and if any get checked then it will add a line through the text
function checkedComplete(event) {
const checkboxElement = event.target;
if (checkboxElement.type === 'checkbox') {
if (checkboxElement.checked) {
checkboxElement.nextElementSibling.style.textDecoration = 'line-through';
// add in moving item
} else {
checkboxElement.nextElementSibling.style.textDecoration = 'none';
}
}
}
// adds deleteItem listener to the list
theList.addEventListener('click', deleteItem);
function deleteItem(event) {
const deleteButton = event.target;
if (deleteButton.type === 'button') {
const deleteParentNode = deleteButton.parentNode;
deleteParentNode.parentNode.removeChild(deleteParentNode);
}
}
You are going to have a storage of you todos, right? Even if you did not think about it, it can do all the work. Just create the array (you could use localStorage to prevent you data from disappearing after browser is restarted) containing your todos and their condition, like
const todos = [{todo:"watch the movie", completed: false}, {...}, {...}]
Now you can easily add or remove items with standard array methods pop&push, delete with splice, filter etc. After array is mdified, just update the page and build your list using Array.map.
You should just add the following logic where you have // add in moving item comment:
const theList = document.getElementById('theNewList');
const lastListItem = theList.children[theList.children.length - 1];
theList.insertBefore(lastListItem, checkboxElement.parentNode.parentNode);
We're selecting your ul and searching for its last li and then we're simply placing the li belonging to the checkboxElement after the last li.
Working example:
// making event listener for adding item
let addBTN = document.getElementById('addBtn');
addBTN.addEventListener('click', addItem);
// this creates a new li based on the entered value in the text box that it gets when you hit the button
// Through Research found that setAttribute isn't really needed and i can just use .id , .type etc
function addItem() {
// Creating needed elements as well as getting text from textbox
let newLi = document.createElement("li");
let myLiValue = document.getElementById('textBoxAdd').value;
let liTextNode = document.createElement("label");
liTextNode.textContent = myLiValue;
// makes div for li
let newDivID = ('div_' + myLiValue);
let newDiv = document.createElement('div');
newDiv.id = newDivID;
// makes checkboxes for the li
let newCheckBoxID = ('checkbox_' + myLiValue);
let newCheckBox = document.createElement('input');
newCheckBox.type = 'checkbox';
newCheckBox.id = newCheckBoxID;
// makes delete button for the li
let newDeleteID = ('deleteButton_' + myLiValue);
let newDeleteButton = document.createElement("button")
newDeleteButton.type = 'button';
newDeleteButton.id = newDeleteID
newDeleteButton.textContent = 'Delete';
//newDeleteButton.setAttribute('onclick', 'deleteItem()');
newDeleteButton.innerHTML = 'Delete';
// appends it to my newDiv
newDiv.appendChild(newCheckBox);
newDiv.appendChild(liTextNode);
newDiv.appendChild(newDeleteButton);
// then appends my new div to the new Li
newLi.appendChild(newDiv);
// this just makes sure a user cant enter in a blank value
if (myLiValue == "") {
alert("Please Enter Something Before Hitting Add Item");
} else {
document.getElementById('theNewList').appendChild(newLi);
document.getElementById('textBoxAdd').value = "";
}
}
//creating event listener for checkbox line through text and moving item
let theList = document.getElementById('theNewList');
theList.addEventListener('click', checkedComplete);
// function that will target every check box in the list and if any get checked then it will add a line through the text
function checkedComplete(event) {
const checkboxElement = event.target;
if (checkboxElement.type === 'checkbox') {
if (checkboxElement.checked) {
checkboxElement.nextElementSibling.style.textDecoration = 'line-through';
const theList = document.getElementById('theNewList');
const lastListItem = theList.children[theList.children.length - 1];
theList.insertBefore(checkboxElement.parentNode.parentNode, lastListItem.nextSilbing);
} else {
checkboxElement.nextElementSibling.style.textDecoration = 'none';
}
}
}
// adds deleteItem listener to the list
theList.addEventListener('click', deleteItem);
function deleteItem(event) {
const deleteButton = event.target;
if (deleteButton.type === 'button') {
const deleteParentNode = deleteButton.parentNode;
deleteParentNode.parentNode.removeChild(deleteParentNode);
}
}
<input id="textBoxAdd" type="text" />
<button id="addBtn" type="button">Add</button>
<ul id="theNewList"></ul>

How to append HTML to a div whilst still allowing for listeners to work?

I have the following HTML I want to append to a div:
var map_div =
"<div class=\"maps\">"
+ "<h2>View marker <button class=\"close\" data-what=\"maps\">[ close ]</button></h2>"
+ "<div class=\"the-contents\" id=\"mapDiv" + link_id + "\"></div>"
+ "</div>";
I can do this with:
document.getElementById("listing"+link_id).insertAdjacentHTML('beforeend', map_div );
This adds the HTML into the div properly - but there is a problem problem with this, in that it doesn't seem to keep the listeners on button.close. This works fine if I put that HTML in directly in the original code (and don't try to to inject it into the DOM). Is there something I'm missing? Do I need to create each individual element, then add it via appendChild, and then add another element into that new element? Seems a bit long winded :/
Thanks
You can add listener to the container and use custom properties to identify the element's role and id:
document.querySelector("button").addEventListener(
"click",
e=>addItem(++startID)
)
var startID=0;
const container = document.getElementById("container");
const addItem = itemID => {
const el = document.createElement("div");
el.innerHTML = `
say hi
remove
${itemID}
`;
el.setAttribute("data-link-container-id",itemID);
container.appendChild(el);
}
const handleClickable = e => {
e.preventDefault();
const id= e.target.getAttribute("data-id");
console.log("hello:",id);
}
const handleRemove = e => {
e.preventDefault();
const id= e.target.getAttribute("data-id");
container.querySelector(`[data-link-container-id="${id}"]`).remove();
}
//add event listener to container and use data-role to see what's clicked
container.addEventListener(
"click",
e=>{
const role = e.target.getAttribute("data-role");
if(role==="say-hi"){
return handleClickable(e);
}
if(role==="remove"){
return handleRemove(e);
}
}
)
<div id="container">
<button>add</button>
</div>

Categories