Reputation: 25
I am building a simple Todo app in Javascript but I got stuck trying to add/remove a class to a List-item(li) that is the parent of a checkbox.
By default a list-item (Todo) checkbox is unchecked (with no class added). Whenever a user check a todo checkbox, a class is added, and the todo text gets a line through.
I managed to make it work but nothing happens.
// ADD ITEM, REMOVE ITEM - FUNCIONALITY
const btn = document.getElementById('btn');
const ulList = document.getElementById('list');
// Button event listener with adding li elemnts with input value
btn.addEventListener('click', function() {
var input = document.getElementById('input').value; // Capture input value
var newItem = document.createElement("LI"); // Create a <li> node
newItem.innerHTML = input + '<input type="checkbox" class="checkboxes" ><p class="delet">x</p>'; // Add content to li element for todo.
ulList.insertBefore(newItem, ulList.childNodes[0]); // Insert <li> before the first child of <ul>
// input = ' '; // Reset input value to empty field
// Remove item funcionality
newItem.childNodes[2].onclick = function() {
this.parentNode.remove(this);
}
})
// ********** IMPORTANT CODE BELOW ***********************
// MARK DONE TODO - FUNCIONALITY
var checkBox = document.getElementsByClassName('checkboxes');
for (var i = 0; i < checkBox; i++) {
checkBox[i].addEventListener('change', function() {
if (this.checked) {
// Checkbox is checked..
this.parentNode.classList.add("line-through");
} else {
// Checkbox is not checked..
this.parentNode.classList.remove("line-through");
}
});
}
.line-through {
text-decoration: line-through;
}
<p class="lead text-center">Welcome to my todoList applications</p>
<div class="row">
<form id="form" class="col-lg-6 col-8 mx-auto">
<div class="input-group">
<input type="text" id="input" class="form-control"><span>
<button id="btn" type="button" class="btn btn-primary">Submit</button></span>
</div>
</form>
</div>
<div class="row">
<ul id="list" class="list col-lg-6 col-8 mx-auto">
<!-- <li>this is a todo item <input type="checkbox" class="checkbox"></li>
<li>this is a todo item <input type="checkbox" class="checkbox"></li> -->
</ul>
</div>
<div class="row">
<button id="btnClr" type="button" class="btn btn-primary mx-auto btnHide">Clear All Todos</button>
</div>
I would appreciate any help. Thanks in advance everyone! :)
Upvotes: 0
Views: 808
Reputation: 141
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<style>
.line-through li {
text-decoration: line-through;
}
</style>
</head>
<body>
<p class="lead text-center">Welcome to my todoList applications</p>
<div class="row">
<form id="form" class="col-lg-6 col-8 mx-auto">
<div class="input-group">
<input type="text" id="input" class="form-control" ><span>
<button id="btn" type="button" class="btn btn-primary">Submit</button></span>
</div>
</form>
</div>
<div class="row">
<ul id="list" class="list col-lg-6 col-8 mx-auto">
<!-- <li>this is a todo item <input type="checkbox" class="checkbox"></li>
<li>this is a todo item <input type="checkbox" class="checkbox"></li> -->
</ul>
</div>
<div class="row">
<button id="btnClr" type="button" class="btn btn-primary mx-auto btnHide">Clear All Todos</button>
</div>
</body>
<script>
// ADD ITEM, REMOVE ITEM - FUNCIONALITY
const btn = document.getElementById('btn');
const ulList = document.getElementById('list');
let checkBox = document.querySelectorAll('.checkboxes li');
// Button event listener with adding li elemnts with input value
btn.addEventListener('click', function(){
var input = document.getElementById('input').value; // Capture input value
var newItem = document.createElement("LI"); // Create a <li> node
newItem.innerHTML = input + '<input type="checkbox" class="checkboxes" ><p class="delet">x</p>'; // Add content to li element for todo.
ulList.insertBefore(newItem, ulList.childNodes[0]); // Insert <li> before the first child of <ul>
// input = ' '; // Reset input value to empty field
// Remove item funcionality
newItem.childNodes[2].onclick = function() {this.parentNode.remove(this);}
});
// ********** IMPORTANTO CODE BELOW ***********************
// MARK DONE TODO - FUNCIONALITY
document.body.addEventListener( 'click', function ( event ) {
if (event.srcElement.className == 'checkboxes') {
console.log(this);
this.classList.toggle('line-through');
}
});
checkBox.forEach(el => {
el.addEventListener('change', myFunction);
}, false);
function myFunction(){
if(this.checked) {
console.log('here')
this.classList.toggle('line-through');
}
}
</script>
</html>
Upvotes: 0
Reputation: 4157
A complete and working example below. Generally speaking, it is easier (for me, but your personal experiences may vary) to utilize document.createElement
instead of .innerHTML
for tasks like yours, because attaching event listeners to elements created by document.createElement
is (again, in my opinion) much easier.
The example creates a new <li>
, <input type="checkbox">
, <span>
(for the todo's title) and a <button>
(for deleting the todo) whenever the "Submit" button is clicked. After all inner elements are created, they are easy to append to the <li>
with .appendChild
.
I tried to use descriptive names, so following along shouldn't be complicated.
const todoAddBtn = document.getElementById('btn');
const todoDeleteBtn = document.getElementById('btnClr');
const todosList = document.getElementById('list');
const todoInput = document.getElementById('input');
todoAddBtn.addEventListener('click', function(){
const todoTopic = readAndClearValue(todoInput);
const todoLi = createListItem();
const todoCheckbox = createCheckbox();
const todoTitle = createTitle(todoTopic);
const todoDelete = createDeleteButton();
todoLi.appendChild(todoCheckbox);
todoLi.appendChild(todoTitle);
todoLi.appendChild(todoDelete);
todosList.insertBefore(todoLi, todosList.firstElementChild);
});
todoDeleteBtn.addEventListener('click', function () {
todosList.innerHTML = '';
});
// readAndClearValue :: HTMLElement -> String
function readAndClearValue (element) {
const value = element.value;
element.value = '';
return value;
}
// createListItem :: () -> HTMLElement
function createListItem () {
return document.createElement('li');
}
// createTitle :: String -> HTMLElement
function createTitle (text) {
const title = document.createElement('span');
title.textContent = text;
return title;
}
// createDeleteButton :: () -> HTMLElement
function createDeleteButton () {
const button = document.createElement('button');
button.textContent = 'X';
button.className = 'delet';
button.addEventListener('click', function () {
button.parentNode.removeChild(button);
// to remove the <li>, use something like
// button.parentNode.parentNode.removeChild(button.parentNode)
// or button.closest('li').remove() if supported
});
return button;
}
// createCheckbox :: () -> HTMLElement
function createCheckbox () {
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.className = 'checkboxes';
checkbox.addEventListener('change', function () {
if (checkbox.checked) {
checkbox.parentNode.classList.add('line-through');
} else {
checkbox.parentNode.classList.remove('line-through');
}
});
return checkbox;
}
.line-through {
text-decoration: line-through;
}
<p class="lead text-center">Welcome to my todoList applications</p>
<div class="row">
<form id="form" class="col-lg-6 col-8 mx-auto">
<div class="input-group">
<input type="text" id="input" class="form-control" >
<button id="btn" type="button" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
<div class="row">
<ul id="list" class="list col-lg-6 col-8 mx-auto">
</ul>
</div>
<div class="row">
<button id="btnClr" type="button" class="btn btn-primary mx-auto btnHide">
Clear All Todos
</button>
</div>
Upvotes: 2
Reputation: 59
Seems like you need to add listeners after checkbox creation. Shat's happening now You load the page and at the start, you don't have any checkbox, so when you run for loop no handlers attached
Here is a sniped of how to make it work. There are a lot of changes but I've tried to leave detailed comments.
Feel free to ask if you have any questions :)
https://codesandbox.io/embed/bootstrap-r3ud0
Also here is JS part.
const btn = document.getElementById("btn");
const ulList = document.getElementById("list");
// Button event listener with adding li elemnts with input value
btn.addEventListener("click", function() {
var input = document.getElementById("input").value; // Capture input value
var newItem = document.createElement("LI"); // Create a <li> node
// manually create input element
var inputEl = document.createElement("input");
// set attributes
inputEl.type = "checkbox";
inputEl.class = "checkboxes";
// also create p element
var xmark = document.createElement("p");
xmark.innerHTML = "x";
xmark.class = "delet";
// set click handler
xmark.onclick = function() {
this.parentNode.remove(this);
};
// most important part!
// we add change listener on input create step
inputEl.addEventListener("change", changeHandler);
newItem.innerHTML = input;
// and append our new elements to the li
newItem.appendChild(inputEl);
newItem.appendChild(xmark);
ulList.insertBefore(newItem, ulList.childNodes[0]); // Insert <li> before the first child of <ul>
});
// create separate handler for change event (first param is event)
const changeHandler = event => {
// we can access checked property of an element
const checked = event.target.checked;
// also we need the target (input in this case) for further manipulations
const element = event.target;
if (checked) {
// Checkbox is checked..
element.parentNode.classList.add("line-through");
} else {
// Checkbox is not checked..
element.parentNode.classList.remove("line-through");
}
};
Upvotes: -1