bindTaskEvents function isn't called 2nd time - javascript

iam making a to do list app just using javascript... in which you can add tasks edit them and delete.but my edit task function is working for the list items with which are there in html document before dom manipulation but after i add new elements it doesn't work for newly appended elements
//Problem: User interaction doesn't provide desired results.
//Solution: Add interactivty so the user can manage daily tasks.
var taskInput = document.getElementById('new-task'); //new-task
var addButton = document.getElementsByTagName('button')[0]; //first button
var incompleteTasksHolder = document.getElementById('incomplete-tasks'); //incompleteTasks
var completedTasksHolder = document.getElementById('completed-tasks'); //completedTasks
//new task list item
var createNewTaskElement = function(taskString){
//create listItem
var listItem = document.createElement('li');
//input(checkbox)
var checkBox = document.createElement('input');
//label
var label = document.createElement('label');
//input(text)
var editInput = document.createElement('input');
//button.edit
var editButton = document.createElement('button');
//button.delete
var deleteButton = document.createElement('button');
//Each element needs modifying
checkBox.type = 'checkbox';
editInput.type = 'text';
editButton.innerText = 'edit';
editButton.className = 'edit';
deleteButton.innerText = 'delete';
deleteButton.className = 'delete';
label.innerText = taskString;
//Each elements needs appended
listItem.appendChild(checkBox);
listItem.appendChild(label);
listItem.appendChild(editInput);
listItem.appendChild(editButton);
listItem.appendChild(deleteButton);
return listItem;
}
//Add a new task
var addTask = function(){
//Create a new list item with the text from #new-task:
var listItem = createNewTaskElement(taskInput.value);
//append listItems to incomplete tasks holder
incompleteTasksHolder.appendChild(listItem);
}
//Edit an existing task
var editTask = function(){
console.log('the edit task function is running');
var listItem = this.parentNode;
var editInput = listItem.querySelector('input[type="text"]');
var label = listItem.querySelector('label');
var containsClass = listItem.classList.contains('editMode');
//if the class of the parent is .editMode
if(containsClass){
//Switch from .editMode
//label text become the input's value
label.innerText = editInput.value;
}else{
//Switch to .editMode
//input value becomes the label's text
editInput.value = label.innerText;
}
//Toggle .editMode on the listItem
listItem.classList.toggle('editMode');
}
//Delete an existing task
var deleteTask = function(){
var listItem = this.parentNode;
var ul = listItem.parentNode;
//Remove the parent list item from the ul
ul.removeChild(listItem);
}
//Mark a task as complete
var taskCompleted = function() {
console.log("Task complete...");
//Append the task list item to the #completed-tasks
var listItem = this.parentNode;
completedTasksHolder.appendChild(listItem);
bindTaskEvents(listItem,taskIncomplete);
}
//Mark a task as incomplete
var taskIncomplete = function(){
//Append the task list item to the #incomplete-tasks
var listItem = this.parentNode;
incompleteTasksHolder.appendChild(listItem);
bindTaskEvents(listItem,taskCompleted);
}
//Set the click handler to the addTask function
addButton.addEventListener("click", addTask);
var bindTaskEvents = function(taskListItem, checkBoxEventHandler) {
console.log("Bind list item events");
//select taskListItem's children
var checkBox = taskListItem.querySelector("input[type=checkbox]");
var editButton = taskListItem.querySelector("button.edit");
var deleteButton = taskListItem.querySelector("button.delete");
//bind editTask to edit button
editButton.onclick = editTask;
//bind deleteTask to delete button
deleteButton.onclick = deleteTask;
//bind checkBoxEventHandler to checkbox
checkBox.onchange = checkBoxEventHandler;
}
//cycle over incompletetaskHolder ul list items
for(var i = 0; i < incompleteTasksHolder.children.length; i++){
//bind events to list's item children(taskCompleted)
bindTaskEvents(incompleteTasksHolder.children[i], taskCompleted);
}
//cycle over completedTasksHolder ul list items
for(var i = 0; i < completedTasksHolder.children.length; i++){
//bind events to list item's children (taskIncomplete)
bindTaskEvents(completedTasksHolder.children[i], taskIncomplete);
}

An event cannot be attached to an undefined Element. Solution: attach events after definition.

Related

checkbox not working like its supposed to

the checkbox is not working like its supposed as it only function when added i was expecting that when i click the checkbox to hide all my books but it only works when i first add another then when the checkbox is clicked is when it hide all my books here is my code:
const addForm = document.forms["add-book"];
addForm.addEventListener("submit", function (e) {
e.preventDefault();
const added = addForm.querySelector('input[type="text"]').value;
//create elements
const li = document.createElement("li");
const bookName = document.createElement("span");
const deleteBtn = document.createElement("span");
//add content
bookName.textContent = added;
deleteBtn.textContent = "delete";
// add classes
bookName.classList.add("name");
deleteBtn.classList.add("delete");
li.appendChild(bookName);
li.appendChild(deleteBtn);
list.appendChild(li);
//hide books
const hideBox = document.querySelector("#hide");
hideBox.addEventListener("change", function (e) {
if (hideBox.checked) {
list.style.display = "none";
} else {
list.style.display = "initial";
}
});
});

How to Remove Particular Element from Array in Javascript with a button

I want to be able to press on my trashcan and be able to delete any todo i want. Right now i can only remove the first index off my todo with with the help off splice. Also when i inspect my array in console i want to be able to use true or false. So when i click on my button my object gets removed from my screen and inside off the array in console it should show that my object indeed have been removed and turns into true instead of false( todoIsRemoved: false). here is a link so you can see all off my code: https://jsfiddle.net/marvstarv/tqdzn8bg/.
this is my class:
let p=0;
let allTheToDos = [];
class Todo{
constructor(toDoItem, removedToDo){
this.Id= p ++;
this.toDoItem = toDoItem;
this.removedToDo = removedToDo;
}
}
this is the function i need help with,
function removeTask (){
let liContainer = document.getElementById ("mySection"); // contains my label(checkmark), Li, and trashcan button.
allTheToDos.splice(Todo,1);
liContainer.innerHTML="";
generateHtml();
console.log(allTheToDos);
}
this function is conected to line 64 off my "main.js": deleteButton.addEventListener('click', () =>{removeTask(allTheToDos[i])});
i appriciate all the help, get back to me if anything was unclear.enter code here
I updated your Js file to:
window.onload = function(){
// without this my site keeps realoding when adding a new item
let firstTask = new Todo ('Bädda sängen',false);
let secondTask = new Todo ('Hänga upp tavlorna',false);
let thirdTask = new Todo ('Kick back & realx',false);
// Adding my premade todo's into my Array that has the variable 'allTheToDos'
allTheToDos.push(firstTask);
allTheToDos.push(secondTask);
allTheToDos.push(thirdTask);
// creating a function so that the user can add a new todo
let addButton = document.getElementById('addBtn');
addButton.addEventListener('click',addNewTask);
generateHtml ();
// let checkedLi = document.getElementById('listItem')
// checkedLi.addEventListener('click',)
console.log(allTheToDos);
}
// my puublic
let p=0;
let allTheToDos = [];
class Todo{
constructor(toDoItem, removedToDo){
this.Id= p ++;
this.toDoItem = toDoItem;
this.removedToDo = removedToDo;
}
}
function generateHtml (){
// Creating an Ul for my items
let section = document.getElementById('mySection');
let myUl = document.createElement('ul');
myUl.className = 'listContainer';
section.appendChild(myUl);
// Creating the loop for my premade todo objects
for(i=0; i<allTheToDos.length; i++){
// Create a div wrapper for my li
let myListWrapperItemContainer = document.createElement('div');
myListWrapperItemContainer.className = "listItemsWrapper";
let id = `to_do_${i}`;
myListWrapperItemContainer.id = id;
// Creating Checked button
let checkedIcon = document.createElement('label');
checkedIcon.className = 'checkedIcon listItemsIcon';
checkedIcon.innerHTML = '<i class="fas fa-check"></i>';
//Creating li
let myLi = document.createElement("li");
myLi.classList = "listItem lineTrough";
myLi.id= "listItem";
// Creating delete button
let deleteButton = document.createElement('button');
deleteButton.id ="deleteButton";
deleteButton.className = 'trashCan listItemsIcon';
deleteButton.innerHTML = '<i class="fas fa-trash-alt"></i>';
// OnClick
deleteButton.addEventListener('click', () => {removeTask(id)});
// Adding everything to my html
myListWrapperItemContainer.appendChild(checkedIcon);
myListWrapperItemContainer.appendChild(myLi);
myListWrapperItemContainer.appendChild(deleteButton);
myLi.innerHTML = allTheToDos[i].toDoItem;
myUl.appendChild(myListWrapperItemContainer);
}
}
function addNewTask (stopRefresh){
stopRefresh.preventDefault();
let liContainer = document.getElementById ("mySection");
let inputValue = document.getElementById('textBox').value;
liContainer.innerHTML="";
if (inputValue == ""){
alert("Type in something");
generateHtml();
}
else{
let newInputValue = new Todo (inputValue);
allTheToDos.push(newInputValue);
generateHtml();
}
}
function removeTask (id){
let element = document.getElementById(id);
let index = allTheToDos.findIndex(e => e.toDoItem === element.textContent);
allTheToDos.splice(index,1);
element.parentNode.removeChild(element);
}

I'm trying to delete a child element, but I keep getting the error node not found

I'm very new to coding so I'm not sure if you get the whole picture with this much, but I can clarify more if this isn't sufficient. Greatly appreciate any help. Thank you!
//remove and complete svg format
var removeSVG = '<svg></svg>';
var completeSVG = '<svg></svg>';
//user clicked on the add button
//If there is any text in the input field add that to the Todo list
document.getElementById('add').addEventListener('click', function () {
var value = document.getElementById('item').value;
if (value) {
addItemTodo(value);
document.getElementById('item').value = "";
}
});
function deleteItem() {
var item = this.parentNode.parentNode;
var parent = this.parentNode;
parent.removeChild(item);
}
function completeItem() {
var item = this.parentNode.parentNode;
var parent = this.parentNode;
var id = parent.id;
var target = (id === 'todo') ? document.getElementById('completed') : document.getElementById('todo');
parent.removeChild(item);
target.insertBefore(item, target.childNodes[0]);
}
//Adds a new item to the todo list
function addItemTodo(text) {
var list = document.getElementById('todo');
var item = document.createElement('li');
item.innerText = text;
var buttons = document.createElement('div');
buttons.classList.add('buttons');
var remove = document.createElement('button');
remove.classList.add('remove');
remove.innerHTML = removeSVG;
//add click event for remove button
remove.addEventListener('click', deleteItem);
var complete = document.createElement('button');
complete.classList.add('complete');
complete.innerHTML = completeSVG;
//add click event for the complete button
complete.addEventListener('click', completeItem);
buttons.appendChild(remove);
buttons.appendChild(complete);
item.appendChild(buttons);
list.insertBefore(item, list.childNodes[0]);
}
//add click event for remove button
remove.addEventListener('click', deleteItem);
So I posted the whole javascript, so you get an idea of the structure, The html itself is just two tags with id=todo and completed respectively
check this https://jsfiddle.net/dp722j27/
function deleteItem() {
var parent = this.parentNode.parentNode.parentNode;
var item = this.parentNode.parentNode;
parent.removeChild(item);
}
If you want to delete button, that was clicked, do like this:
function deleteItem(event) {
var item = event.target.parentNode;
var parent = event.target.parentNode.parentNode;
parent.removeChild(item);
}
//add click event for remove button
document.addEventListener('click', deleteItem);

Appending a menu to DOM on click with subsequent clicks creating only list item

I'm building a Javascript menu builder which allows a user to items to a menu by filling inputs and then submit the data.
The idea is that on the first click the whole menu is placed in the DOM including the first list item. Subsequent clicks will only put an <li> element in the DOM.
Here is my code. The functions you should look at are createForm and createMenu:
Codepen example
// track the button clicks globally
var btnClicks = 0;
// create a function the append the form to the DOM on page load.
window.onload = createForm;
function createForm() {
// initialize out form container
var f = formContainer();
// declare the variables we will use in one place
var itemName, id, className, target, href, btn, text, h2;
// create an array that will hold the values we wish to pass to our links
// in the menu
var listItem = [itemName, id, className, target, href];
// Create the labels so each input can have a unique label
var labels = ['Name', 'ID', 'Class', 'Target', 'Link'];
// Helper text
h2 = document.createElement('h2');
text = 'Fill in the fields below to create a new menu item';
h2.innerText = text;
f.appendChild(h2);
// loop through the list items
listItem.forEach(function(element, index){
// for each element, call the createDOMNode function and pass in required data
element = createDOMNode(labels[index], 'input', 'text', 'input_' + index);
// append each element to the form container
f.appendChild(element);
});
// create the button and give it an onclick handler
btn = document.createElement('button');
btn.innerText = 'Create Menu Item';
f.appendChild(btn);
btn.onclick = getUserInput;
}
// get what the user inputted into the fields
function getUserInput() {
// initialize some variables and an array
var itemName, id, className, target, href;
item = [];
// access the values from the input fields
values = [
itemName = document.getElementById('input_0').value,
id = document.getElementById('input_1').value,
className = document.getElementById('input_2').value,
target = document.getElementById('input_3').value,
href = document.getElementById('input_4').value,
];
// loop through each value
values.forEach(function(element, index){
// and push it into the item[] array
if(element !== '') {
item.push(element);
}
});
// make sure required items are filled out
if(values[0] === '' && values[4] === '') {
alert('Name and Link are both required');
} else if(values[0] === '') {
alert('Name is required');
} else if(values[4] === '') {
alert('Link is required');
}
// if the array is not empty, then create the menu
if(item.length !== 0) {
createMenu(item);
}
// increase the button counter
btnClicks += 1;
}
// function to create a new menu
function createMenu(item) {
// create elements needed for menu
var nav = document.createElement('nav');
var ul = document.createElement('ul');
var li = document.createElement('li');
var a = document.createElement('a');
var f = document.getElementById('formContainer');
// trying to create the nav only on the first click
if(btnClicks < 1) {
nav.setAttribute('class', 'nav');
document.body.insertBefore(nav, f);
nav.appendChild(ul);
}
var arrayLength = item.length;
// loop through items and set their attributes
for(var i = 0; i < arrayLength; i++) {
li.appendChild(a);
a.innerText = item[0];
a.setAttribute('id', item[1]);
a.setAttribute('class', item[2]);
a.setAttribute('target', item[3]);
a.setAttribute('href', item[4]);
}
// and append them to the ul
ul.appendChild(li);
}
function formContainer() {
// create the element and set where it is displayed in the DOM
var formContainer = document.createElement('div');
formContainer.setAttribute('id', 'formContainer');
var scriptTag = document.getElementsByTagName('script')[0];
document.body.insertBefore(formContainer, scriptTag);
// style the element
formContainer.style.width = '360px';
formContainer.style.margin = '0 auto';
formContainer.style.border = '1px solid #ddd';
formContainer.style.padding = '15px';
return formContainer;
}
function createDOMNode(label, element, type, id) {
var l = document.createElement('label');
l.innerText = label;
// create the node and set it's type attribute
var node = document.createElement(element);
node.setAttribute('type', type);
node.setAttribute('id', id);
// style the node
node.style.padding = '8px 4px';
node.style.width = '100%';
node.style.marginBottom = '10px';
node.style.boxSizing = 'border-box';
l.appendChild(node);
return l;
}
You are creating a new <nav> and <ul> every time you click the button and appending the new <li> item to it. You only add the first <nav> created to the document so all others are being created and never added. Move your variables for creating your <nav> and <ul> outside function createMenu(item)
<script>
// track the button clicks globally
var btnClicks = 0;
// create a function the append the form to the DOM on page load.
window.onload = createForm;
function createForm() {
// initialize out form container
var f = formContainer();
// declare the variables we will use in one place
var itemName, id, className, target, href, btn, text, h2;
// create an array that will hold the values we wish to pass to our links
// in the menu
var listItem = [itemName, id, className, target, href];
// Create the labels so each input can have a unique label
var labels = ['Name', 'ID', 'Class', 'Target', 'Link'];
// Helper text
h2 = document.createElement('h2');
text = 'Fill in the fields below to create a new menu item';
h2.innerText = text;
f.appendChild(h2);
// loop through the list items
listItem.forEach(function(element, index){
// for each element, call the createDOMNode function and pass in required data
element = createDOMNode(labels[index], 'input', 'text', 'input_' + index);
// append each element to the form container
f.appendChild(element);
});
// create the button and give it an onclick handler
btn = document.createElement('button');
btn.innerText = 'Create Menu Item';
f.appendChild(btn);
btn.onclick = getUserInput;
}
// get what the user inputted into the fields
function getUserInput() {
// initialize some variables and an array
var itemName, id, className, target, href;
item = [];
// access the values from the input fields
values = [
itemName = document.getElementById('input_0').value,
id = document.getElementById('input_1').value,
className = document.getElementById('input_2').value,
target = document.getElementById('input_3').value,
href = document.getElementById('input_4').value,
];
// loop through each value
values.forEach(function(element, index){
// and push it into the item[] array
if(element !== '') {
item.push(element);
}
});
// make sure required items are filled out
if(values[0] === '' && values[4] === '') {
alert('Name and Link are both required');
} else if(values[0] === '') {
alert('Name is required');
} else if(values[4] === '') {
alert('Link is required');
}
// if the array is not empty, then create the menu
if(item.length !== 0) {
createMenu(item);
}
// increase the button counter
btnClicks += 1;
}
var nav = document.createElement('nav');
var ul = document.createElement('ul');
// function to create a new menu
function createMenu(item) {
// create elements needed for menu
var li = document.createElement('li');
var a = document.createElement('a');
var f = document.getElementById('formContainer');
// trying to create the nav only on the first click
if(btnClicks < 1) {
nav.setAttribute('class', 'nav');
document.body.insertBefore(nav, f);
nav.appendChild(ul);
}
var arrayLength = item.length;
// loop through items and set their attributes
for(var i = 0; i < arrayLength; i++) {
li.appendChild(a);
a.innerText = item[0];
a.setAttribute('id', item[1]);
a.setAttribute('class', item[2]);
a.setAttribute('target', item[3]);
a.setAttribute('href', item[4]);
}
// and append them to the ul
ul.appendChild(li);
}
function formContainer() {
// create the element and set where it is displayed in the DOM
var formContainer = document.createElement('div');
formContainer.setAttribute('id', 'formContainer');
var scriptTag = document.getElementsByTagName('script')[0];
// document.body.insertBefore(formContainer, scriptTag);
document.body.appendChild(formContainer);
// style the element
formContainer.style.width = '360px';
formContainer.style.margin = '0 auto';
formContainer.style.border = '1px solid #ddd';
formContainer.style.padding = '15px';
return formContainer;
}
function createDOMNode(label, element, type, id) {
var l = document.createElement('label');
l.innerText = label;
// create the node and set it's type attribute
var node = document.createElement(element);
node.setAttribute('type', type);
node.setAttribute('id', id);
// style the node
node.style.padding = '8px 4px';
node.style.width = '100%';
node.style.marginBottom = '10px';
node.style.boxSizing = 'border-box';
l.appendChild(node);
return l;
}
</script>

Add style color to an appended li - Javascript

When adding a new item to the list i would like it to change color based on the input value.
How do i add the input.value to my new appended li item?
I have created a codepen if anyone would be able to help
http://codepen.io/o-sewell/pen/mOBjvQ
// DIV TOGGLE
const toggleButton = document.querySelector('#toggleList');
const listDiv = document.querySelector('.list');
// User INPUT
const userInput = document.querySelector('.userInput');
const button = document.querySelector('button.description');
const p = document.querySelector('p.description');
let listItem = document.querySelectorAll('li');
// ADD ITEM
const addItemInput = document.querySelector('.addItemInput');
const addItemButton = document.querySelector('button.addItemButton');
button.addEventListener('click', () => {
for (let i = 0; i < listItem.length; i++) {
listItem[i].style.color = userInput.value;
}
p.innerHTML = userInput.value + ':';
});
toggleButton.addEventListener('click', () => {
if (listDiv.style.display == 'none') {
listDiv.style.display = 'block';
toggleButton.textContent = 'Hide list';
} else {
listDiv.style.display = 'none';
toggleButton.textContent = 'Show list';
}
});
addItemButton.addEventListener('click', () => {
let list = document.querySelector('ul');
let li = document.createElement('li');
li.textContent = addItemInput.value;
let appendedItem = list.appendChild(li);
console.log(appendedItem);
for (let i = 0; i < appendedItem.length; i++) {
appendedItem[i].style.color = userInput.value;
}
console.log(appended item);
addItemInput.value = '';
});
Here is the new one :
//DIV TOGGLE
const toggleButton = document.querySelector('#toggleList');
const listDiv = document.querySelector('.list');
var lastPickedColor = "black"; // it will store the last picked color
//User INPUT
const userInput = document.querySelector('.userInput');
const button = document.querySelector('button.description');
const p = document.querySelector('p.description');
let listItem = document.querySelectorAll('li');
//ADD ITEM
const addItemInput = document.querySelector('.addItemInput');
const addItemButton = document.querySelector('button.addItemButton');
button.addEventListener('click', () => {
lastPickedColor = userInput.value;
for(let i = 0; i < listItem.length; i++) {
listItem[i].style.color = lastPickedColor;
}
p.innerHTML = userInput.value + ':';
});
toggleButton.addEventListener('click', () => {
if (listDiv.style.display == 'none') {
listDiv.style.display = 'block';
toggleButton.textContent = 'Hide list';
} else {
listDiv.style.display = 'none';
toggleButton.textContent = 'Show list';
}
});
addItemButton.addEventListener('click', () => {
let list = document.querySelector('ul');
let li = document.createElement('li');
li.style.color = lastPickedColor; // so it will add li with last picked color
li.textContent = addItemInput.value;
let appendedItem = list.appendChild(li);
console.log(appendedItem);
for(let i = 0; i < appendedItem.length; i++) {
appendedItem[i].style.color = userInput.value;
}
console.log(appendeditem);
addItemInput.value = '';
});
I added lastPickedColor variable to top of the page whenever you change the color, it will store the lastPickedColor and all list add with this color.
The answer is quite simple.
You're defining the initial list when the js is executed, but the new element is created dynamically. Therefore, you'll have to create a new nodelist based on all elements, including the new one. You can simply redefine it inside the current click event:
button.addEventListener('click', () => {
listItem = document.querySelectorAll('li');
for(let i = 0; i < listItem.length; i++) {
listItem[i].style.color = userInput.value;
}
p.innerHTML = userInput.value + ':';
});
To give it the same color as the existing ones, change this function to apply the styling directly on the latest added element:
addItemButton.addEventListener('click', () => {
let list = document.querySelector('ul');
let li = document.createElement('li');
li.textContent = addItemInput.value;
let appendedItem = list.appendChild(li);
appendedItem.style.color = userInput.value;
addItemInput.value = '';
});
Working codepen example
so I have made 2 changes for your code to work. Please see
http://codepen.io/amoolya/pen/GNMXqa?editors=1111
First change
addItemButton.addEventListener('click', () => {
let list = document.querySelector('ul');
let li = document.createElement('li');
li.textContent = addItemInput.value;
let appendedItem = list.appendChild(li);
appendedItem.style.color = list.firstElementChild.style.color; //The newly appended item is given the color of the first list element. In your case, this would be whatever color the user chose last.
});
Second Change:
I added
document.querySelectorAll('li');
inside your event handler for the color change button so that each time, the length of the new list is calculated.

Categories