How to use a forEach loop on dynamically created elements - javascript

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!

Related

How to create an object of an <li> element?

I created a To-Do List with JS. I'm mostly finished but my last task is to create a local storage. The purpose of that is, that the Tasks should not vanish, when I reload the page. I know that I have to create an array of objects for that, but I don't know how to represent my <'li'> element as an object. So how do I represent my Tasks as an object to create an array
add.addEventListener("click", function (){
// create task text
if(task.value != ""){
let li = document.createElement('li');
ul.appendChild(li);
// create checkbox
let box = document.createElement('input');
box.setAttribute('type', 'checkbox');
li.appendChild(box);
box.classList.add('check');
//create span
let span = document.createElement('span');
li.appendChild(span);
span.innerHTML= task.value;
span.classList.add('task');
// create delete button
let btn = document.createElement('button');
li.appendChild(btn);
btn.classList.add("delete-btn");
btn.innerHTML = 'x';
btn.onclick = function () {
btn.parentElement.remove();
};
});

How do I add an event listener to a dynamically created HTMLcollection?

I am creating a small library app and I am trying to add a delete entry function. I am looking to add the actual functionality of the button but I can't figure out how to add the eventlistener to each instance. When I click on the image it does nothing.
JAVASCRIPT
//dynamic creation of trash button
let deleteEntry = document.createElement("td")
const deleteBtn = document.createElement("input");
const deleteImg = document.createAttribute("type");
deleteImg.value = "image";
deleteBtn.className = "deletebtn"
deleteBtn.src = "red_trash_can.png";
deleteEntry.append(deleteBtn);
deleteBtn.setAttributeNode(deleteImg)
//
const trashBtn = Array.from(document.getElementsByClassName("deletebtn"));
trashBtn.forEach(button => {
button.addEventListener('click',e => {
console.log(e)
})
})
HTML
<td>
<input class="deletebtn" src="red_trash_can.png" type="image">
</td>
You can add the event listener directly to the deleteBtn before appending it. And also the type can be added directly like you do with the className or the src.
Working example: (i used a dummy image and a simple string in console.log() because the event took so long)
//dynamic creation of trash button
const deleteEntry = document.createElement("td")
const deleteBtn = document.createElement("input");
deleteBtn.type = "image";
deleteBtn.className = "deletebtn"
deleteBtn.src = "https://picsum.photos/200";
deleteBtn.addEventListener('click', () => {
console.log("You clicked the image!")
});
deleteEntry.append(deleteBtn);
document.querySelector("#wrapper").append(deleteEntry);
<div id="wrapper"></div>

How to access elements that were created in script?

In the function below, I create cards that have a toggle button and remove button. However, when I try to access the buttons with a queryselector, I get null. Anyone know how to access elements that were created in a different file? I can provide the full files if needed.
function updateDisplay() {
for (i = 0; i < myLibrary.length; i++){
let div = document.createElement('div');
let title = document.createElement('h5');
let author = document.createElement('h5');
let pages = document.createElement('h5');
let isRead = document.createElement('h5');
let removeButton = document.createElement('button');
let toggleButton = document.createElement('button');
div.classList.add('card');
title.classList.add('title');
author.classList.add('author');
pages.classList.add('pages');
isRead.classList.add('isRead');
removeButton.classList.add('removeButton');
toggleButton.classList.add('toggleButton');
title.textContent = myLibrary[i].title;
author.textContent = myLibrary[i].author;
pages.textContent = `${myLibrary[i].pages} pages`;
isRead.textContent = myLibrary[i].isRead ? 'Read' : 'Unread';
removeButton.textContent = 'Remove';
toggleButton.textContent = 'Toggle Read Status';
Your problem is obvious you're creating the elements but not appending the into the body.
you'll have to append each element you create like the following example:
var element= document.createElement("div"); // Create a <div> element
element.innerHTML = "some content"; // Insert text
document.body.appendChild(element); // Append <div> to <body>
note that you can do document.body.append(div,title,author,pages,isRead,removeButton,toggleButton);
to append them all in one line

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 add remove functionality to my todo app?

Jsfiddle Here
const btnTodo = document.querySelector('.btnTodo');
const todoList = document.querySelector('.todoList');
const removeBtn = document.querySelector('.btnRemove');
btnTodo.addEventListener('click', () => {
var todoText = txtTodo.value.trim();
var listItem = document.createElement('p');
listItem.innerHTML = todoText + ' <button class="btn btnRemove"><i class="big fas fa-trash"></i></button>' + '<br>';
todoList.append(listItem);
txtTodo.innerText = null;
});
removeBtn.addEventListener('click', () => {
//WHAT TO DO??
});
I am in high school. I wanted to include a remove functionality to my todo app. I made this by the knowledge given to me by my school.
Okay so there are various ways of doing this, here's one of the ways:
let txtTodo = document.querySelector('.txtTodo');
let btnTodo = document.querySelector('.btnTodo');
let todoList = document.querySelector('.todoList');
btnTodo.addEventListener('click', () => {
let todoText = txtTodo.value.trim();
let listItem = document.createElement('p');
listItem.innerHTML = todoText + ' <button class="btn btnRemove"><i class="big fas fa-trash"></i></button>' + '<br>';
todoList.append(listItem);
txtTodo.innerText = null;
listItem.querySelector('.btnRemove').addEventListener("click", () => {
listItem.remove()
});
});
Fiddle
One thing to keep in mind is that storing all remove buttons in a variable beforehand (as you attempted in line 3) isn't going to work because is isn't a live collection and thus when you append new buttons in the DOM then the stored collection would become stale. Hence it's a good idea to add event listeners to newly appended elements as I did above.
Also you should be doing txtTodo.value = ""; instead of txtTodo.innerText = null; to clear the input.
Good luck!

Categories