I'm working on a simple todo list app in JavaScript.
I need to add new items from the input field on a list, and have the ability to remove items from the list.
I wrote all the necessary functions for storing values from input field and adding it to a list. However I am not able to make removing elements work.
HTML, CSS and JS are provided. removeItems() function doesn't work.
const enterBtn = document.getElementById('enter');
const input = document.querySelector('input');
const ulList = document.querySelector('ul');
const delBtn = document.querySelector('button');
// Get input from the input field and create a new li itemn in ul list
function inputLength() {
return input.value.length;
}
function addDelButtonsToExistingItems() {
const liItems = document.querySelectorAll('li');
for (let i = 0; i < liItems.length; i++) {
const delBtn = document.createElement('button');
delBtn.appendChild(document.createTextNode('delete'));
liItems[i].appendChild(delBtn);
}
}
// Add delete buttons to already existing items in a document
addDelButtonsToExistingItems();
// to add new items from the input field
function addNewItem() {
if (inputLength() > 0) {
const newLiItem = document.createElement('li');
newLiItem.appendChild(document.createTextNode(input.value));
const delBtn = document.createElement('button');
delBtn.appendChild(document.createTextNode('delete'));
newLiItem.appendChild(delBtn);
ulList.appendChild(newLiItem);
input.value = '';
}
}
/**
* To remove items from the list on delete btn pressed
*/
function removeItems(e) {
if (e.target.tagName === 'BUTTON') {
e.target.classList.toggle('remove');
}
}
/**
* To toggle done class on list items clicked
*/
function toggleDone(e) {
if (e.target.tagName === 'LI') {
e.target.classList.toggle('done');
}
}
// If enter key is pressed
function addOnEnterKeyPressed(e) {
if (e.keyCode === 13) {
addNewItem();
}
}
// To add new items on enter btn clicked
enterBtn.addEventListener('click', addNewItem);
// To add new items on enter key pressed
input.addEventListener('keypress', addOnEnterKeyPressed);
// To toggle the done class
ulList.addEventListener('click', toggleDone);
// To remove the items on del btn click
// ???????
.done{
text-decoration: line-through;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Shipping List</title>
</head>
<body>
<h1>Shopping List</h1>
<h3>Get it done today</h3>
<input type="text" name="input" id="input">
<button id="enter">Enter</button>
<ul>
<li>NoteBook</li>
<li>Pens</li>
<li>Eraser</li>
<li>CMPSC 412 book</li>
</ul>
<script src="script.js"></script>
</body>
</html>
Updated the code a bit to get rid of some of the const and simplified the way the delete button click event target is found.
Mostly, I've added a class delete to the delete buttons, so we can target those specifically without relying on the tag.
I commented the code I changed.
const enterBtn = document.getElementById('enter');
const input = document.querySelector('input');
const ulList = document.querySelector('ul');
// Get input from the input field and create a new li itemn in ul list
function inputLength() {
return input.value.length;
}
function addDelButtonsToExistingItems() {
const liItems = document.querySelectorAll('li');
for (let i = 0; i < liItems.length; i++) {
const delBtn = document.createElement('button');
// Let's add a class so we can target it instead of using a tag name.
delBtn.classList.add('delete');
delBtn.appendChild(document.createTextNode('delete'));
liItems[i].appendChild(delBtn);
}
}
// Add delete buttons to already existing items in a document
addDelButtonsToExistingItems();
// to add new items from the input field
function addNewItem() {
if (inputLength() > 0) {
const newLiItem = document.createElement('li');
newLiItem.appendChild(document.createTextNode(input.value));
const delBtn = document.createElement('button');
// Adding a class again.
delBtn.classList.add('delete');
delBtn.appendChild(document.createTextNode('delete'));
newLiItem.appendChild(delBtn);
ulList.appendChild(newLiItem);
input.value = '';
}
}
/**
* To remove items from the list on delete btn pressed. We will do this looking at the document.
*/
function removeItem(e) {
// Since we are watching the document for a click, we can see if the target of the event has the class "delete" and we can remove its parent li.
if (e.target.classList.contains('delete')) {
e.target.parentNode.remove();
}
}
/**
* To toggle done class on list items clicked
*/
function toggleDone(e) {
if (e.target.tagName === 'LI') {
e.target.classList.toggle('done');
}
}
// If enter key is pressed
function addOnEnterKeyPressed(e) {
if (e.keyCode === 13) {
addNewItem();
}
}
// To add new items on enter btn clicked
enterBtn.addEventListener('click', addNewItem);
// To add new items on enter key pressed
input.addEventListener('keypress', addOnEnterKeyPressed);
// To toggle the done class
ulList.addEventListener('click', toggleDone);
// To remove the items on del btn click. We are targeting the document, since the delete elements can update.
document.addEventListener('click', removeItem);
.done {
text-decoration: line-through;
}
<h1>Shopping List</h1>
<h3>Get it done today</h3>
<input type="text" name="input" id="input">
<button id="enter">Enter</button>
<ul>
<li>NoteBook</li>
<li>Pens</li>
<li>Eraser</li>
<li>CMPSC 412 book</li>
</ul>
Related
It is supposed to work like this. After I create an item on my list, it should be marked as done with a line-through after clicked and deleted after the delete button is clicked. All this is fine BUT what I really want is to be able to toggle the item as many times as I want. As you can see when I toggle the item it creates a new delete button instead.
function createListElement() {
var li = document.createElement("li"); // you need to create a node first
li.appendChild(document.createTextNode(input.value)); // then append it to the ul
ul.appendChild(li);
input.value = ""; // clear input after submit
// mark it as done
li.addEventListener("click", function () {
li.classList.toggle("done");
//create delete button
var delete_btn = document.createElement("button");
delete_btn.innerHTML = "Delete";
li.appendChild(delete_btn);
// removes element
delete_btn.addEventListener("click", function () {
li.parentNode.removeChild(li);
});
});
}
I'm adding a codepen so you can take a look at it. Thanks.
Check if there'a already a delete button, and don't add another one if so.
var button = document.getElementById("enter");
var input = document.getElementById("userInput");
var ul = document.querySelector("ul");
// If a button is clicked
button.addEventListener("click", addListAfterClick);
// If input is keypressed
input.addEventListener("keypress", addListAfterKeypress);
function inputLength() {
return input.value.length;
}
// create list and button elements
function createListElement() {
var li = document.createElement("li"); // you need to create a node first
li.appendChild(document.createTextNode(input.value)); // then append it to the ul
ul.appendChild(li);
input.value = ""; // clear input after submit
// mark it as done
li.addEventListener("click", function() {
li.classList.toggle("done");
if (!li.querySelector("button.delete")) {
//create delete button
var delete_btn = document.createElement("button");
delete_btn.innerHTML = "Delete";
delete_btn.classList.add("delete");
li.appendChild(delete_btn);
// removes element
delete_btn.addEventListener("click", function() {
li.parentNode.removeChild(li);
});
}
});
}
function addListAfterClick() {
if (inputLength() > 0) {
createListElement();
}
}
function addListAfterKeypress(event) {
if (inputLength() > 0 && event.key === "Enter") {
createListElement();
}
}
body {
#import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght#300&display=swap');
font-family: 'Plus Jakarta Sans', sans-serif;
}
li {
cursor: pointer;
padding: 5px 0;
}
li>button {
margin-left: 10px;
}
.done {
text-decoration: line-through;
}
<h1>Shopping List</h1>
<p><small>Click item to mark it as <strong>done.</strong></small></p>
<input id="userInput" type="text" placeholder="enter items">
<button id="enter">Enter</button>
<ul>
</ul>
I've been attempting to prevent duplicate items from being added to a shopping list I created. I thought the code directly below might do it, but it hasn't been helping. I expected the JS code to access the ul tag and iterate through it's list items so it can compare what's already in the list to what's trying to be added. I'm assuming my issue is with accessing the input. Also, which function should this be done in? The createListElement function or the addListAfterClick function. Please see the code snippet for how the code runs. Thank you in advance!!
var ul = document.getElementById("shoppingcart");
var items = ul.getElementsByTagName("li");
for (var i = 0; i < items.length; i++) {
if (input.value === items[i]); {
alert("already in list");
console.log("already in list");
}
var button = document.getElementById("enter");
var input = document.getElementById("userinput");
var ul = document.querySelector("ul");
var li = document.querySelectorAll("li");
function inputLength() {
return input.value.length; // gets the length of the input value
}
function createListElement() {
var li = document.createElement("li"); // creating a new li tag
li.appendChild(document.createTextNode(input.value)); // adding input value to newly created li tag
li.classList.add("item"); // adds a class called "item" to the newly created li tag
li.addEventListener("click", toggleDone);
/* adds click abilities to new item by referencing the toggleDone function, this allows for a line-through
an item in the list if it's clicked,and will remove the line if the item is clicked again */
ul.appendChild(li); // adds the li tag in the ul tag
input.value = ""; //clears the input box to allow for new inputs
var button = document.createElement("button"); // creates anew button tag
button.appendChild(document.createTextNode("delete")); // creates a new button with the word "delete"
li.append(button); //adds delete button next to each new input in the shopping list
button.onclick = removeParent; // if the delete button is clicked, it references the removeParent function which deletes the an input from the shopping list
}
function removeParent(event) {
event.target.parentNode.remove(); // simply deletes an input from the parent node, which is the ul tag, remember li tags are children of ul tags
}
function toggleDone() {
this.classList.toggle('done'); // references the css ".done" class, which allows for line-through text, the toggle function turns on and off the line through
}
function addListAfterClick() {
if (inputLength() > 0) //if the length of the input is greater than 0..(meaning you have to input a something in order to add to the list)
{
createListElement(); // references createListElement function, which adds a new li tag with the input value and a delete button next to it
}
}
function addListAfterKeyPress(event) {
if (inputLength() > 0 && event.keyCode === 13) // if length of the input is greater than 0 and your press the enter key (13 is the value that represents the enter key)
{
{
createListElement(); // references createListElement function, which adds a new li tag with the input value and a delete button next to it
}
}
}
button.addEventListener("click", addListAfterClick);
input.addEventListener("keypress", addListAfterKeyPress);
<!DOCTYPE html>
<html>
<head>
<title> JavaScript + DOM </title>
</head>
<body>
<h1> Shopping List</h1>
<p id="first"> Get it done today</p>
<input id="userinput" type="text" placeholder="enter items">
<button id="enter">Click Me!</button>
<ul class="list" id="shoppingcart">
<li class="bold red" random="23"> Notebook</li>
</ul>
</body>
</html>
A quick solution is to use a "Set", you can check the duplicity in simple steps. For example:
var mySet = new Set();
mySet.add("foo");
mySet.add("fuu");
mySet.has("foo"); // true
mySet.has("fuu"); // true
mySet.has("faa"); // false
So, you can add a set in your first lines and set the conditional has in addListAfterClick:
function addListAfterClick() {
if (inputLength() > 0 && !mySet.has(input.value))
{
createListElement();
}
}
And add the value when input is done in createListElement:
function createListElement()
{
// code...
ul.appendChild(li);
mySet.add(input.value);
input.value = "";
// code...
}
I am facing a bit of trouble here. I am trying to create a to-do list with local storage but the only things I got to work are adding list item it to the local storage and deleting all items from the local storage but I can't delete a single SELECTED item out from the list. Can someone help me figure:
1) Removing a selected single item from the list.
2) Putting Checkbox before the List Text.
3) On clicking checkbox, toggle class list "strike" and remembering it on load/page refresh.
Here is my code:
<body>
<div>
<h1>To-do's list</h1>
<div>
<input type="text" id="textBox">
<button id="enterBtn" type="button">Enter</button>
<div>
<uL id="ul">
<li class="li"><input type="checkbox" class="checkBox" name=""> Buy food for Siboo <button class="deleteBtn">Delete</button></li>
<li class="li"><input type="checkbox" class="checkBox" name=""> Get a new controller <button class="deleteBtn">Delete</button></li>
</uL><br>
<button id="deleteAllBtn"><i class="fa fa-trash"></i> Delete All Items</button>
</div>
<script type="text/javascript" src="script.js"></script>
</div>
</body>
Here is CSS:
.strike {
text-decoration: line-through;
}
Here is my JS:
var textBox = document.getElementById("textBox");
var enterBtn = document.getElementById("enterBtn");
var ul = document.querySelector("ul");
var li = document.getElementsByClassName("li");
var checkBox = document.getElementsByClassName("checkBox");
var deleteBtn = document.getElementsByClassName("deleteBtn");
var deleteAllBtn = document.getElementById("deleteAllBtn");
var itemsArray = localStorage.getItem('items') ? JSON.parse(localStorage.getItem('items')) : [];
localStorage.setItem('items', JSON.stringify(itemsArray));
var data = JSON.parse(localStorage.getItem('items'));
// Functions *********************
// Adding New Items to List - adding list element with checkbox and delete button *********************
function addNewItemToList(text)
{
itemsArray.push(textBox.value);
localStorage.setItem('items', JSON.stringify(itemsArray));
liMaker(textBox.value);
}
function liMaker(text) {
var newLi = document.createElement("li");
newLi.textContent = text;
newLi.className = "li";
ul.appendChild(newLi);
var createCheckBox = document.createElement("input");
createCheckBox.type = "checkbox";
createCheckBox.className = "checkBox";
newLi.appendChild(createCheckBox);
var createDeleteButton = document.createElement("button");
var nameButtonDelete = document.createTextNode("Delete");
createDeleteButton.appendChild(nameButtonDelete);
createDeleteButton.className = "deleteBtn";
newLi.appendChild(createDeleteButton);
}
data.forEach(item => {
liMaker(item);
});
// CheckBox ELEMENT - click on checkBox to strike the list item off list*********************
function checkBoxFunction() {
for (var i = 0; i < checkBox.length; i++) {
checkBox[i].onclick = function () {
this.parentNode.classList.toggle("strike");
}}
}
// // DELETE BUTTON - click the delete button to delete the list item *********************
function deleteBtnFunction() {
for (var i = 0; i < deleteBtn.length; i++) {
deleteBtn[i].onclick = function () {
this.parentNode.parentNode.removeChild(this.parentNode);
}}
}
// DELETE ALL BUTTON - click the Delete ALl Items button to remove all items from the list *********************
function deleteAllBtnFunction()
{
localStorage.clear();
while (ul.firstChild) {
ul.removeChild(ul.firstChild);
}
itemsArray = [];
}
// TEXTBOX - press enter key to add an item to list *********************
function textBoxFunction()
{
if (event.keyCode === 13 && textBox.value.length > 0)
{
addNewItemToList();
textBox.value = "";
}
else if (event.keyCode === 13)
{
alert("Please enter an item to-do!");
}
}
// ENTER BUTTON - click the enter button to add item to list *********************
function enterBtnFunction()
{
if (textBox.value.length > 0)
{
addNewItemToList();
textBox.value = "";
}
else
{
alert("Please enter an item to-do!");
}
}
listItemFunction();
deleteBtnFunction();
// Event Listeners *********************
textBox.addEventListener("keypress", textBoxFunction);
enterBtn.addEventListener("click", enterBtnFunction);
deleteAllBtn.addEventListener("click", deleteAllBtnFunction);
// End of Event Listeners *********************
localStorage.removeItem(/*key*/);
You can use this function to remove a specific item and save the items to localstorage again:
function removeItem(text) {
var items = JSON.parse(localStorage.getItem("items"));
items = items.filter(function(e) {return e !== text; });
localStorage.setItem("items", JSON.stringify(items));
}
removeItem("itemname");
I am not a professional but recently I created a todo app like this. I figured out after adding a new item, action listeners do not count them. So I inserted the event listeners inside the new element creation function after adding the element to the DOM.
Here is my suggestion
First, you have to link jQuery-3.2.1.slim.min.js to your project
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
Put this code to the end of your liMaker function.
$('input[type="checkbox"]').click(function() {
if ($(this).is(':checked')) {
//Call your function
console.log(this.id)
}
});
When creating the list element give each of them a unique id (add it to your checkbox). Maybe you can give them the array index.
createCheckBox.id ={unique id}
After doing that when you click the checkbox to invoke the above function. In that function this.id is the unique value you gave to the checkbox.
Then you can use it to remove that index from your array.
Second Method (Pure JS)
Give a common class name to all checkboxes and a unique id also.
const checkBoxes = document.querySelectorAll(".{common_class_name}")
for(const cb of checkBoxes){
button.addEventListener('click',function(){
let id = this.id
/*this id is the value you gave to the checkbox.
Call your deleting methods here*/
})
}
Place this method at the end of your element creation method. This must run every time you add an element.
I copied your code and my method is working fine. I am a beginner there may be better ways to do that. but my method works.
Good Luck
I am trying to add an event listener on my JavaScript file to link it to my HTML however, it does not recognise it. Am I doing something wrong because they are in separate text documents. However, it works with my other sections so I think it might be a small mistake that I have not spotted out yet.
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<head>
<title>Outliner</title>
<link href="style.css" rel="stylesheet" title="Style">
<script type= "text/javascript" src='setting.js'></script>
<div>
<input type="button" onclick="reset();" value="Reset form">
<button id="pr" onclick="reset()"> Clear </button>
<div id="ul" class="editor" contenteditable="true" draggable="true" ></div>
</div>
</head>
</body>
const TAB_KEY = 9;
const ENTER_KEY = 13;
let editor = document.querySelector('.editor');
editor.appendChild(document.createElement('li')); // Add initial (for visibility)
window.editor.addEventListener('focus', (e) => {
//let ul = e.target;
//let li = document.createElement('li');
//ul.appendChild(li);
});
window.editor.addEventListener('keydown', (e) => {
let code = e.keyCode || e.which;
if (code == TAB_KEY) {
e.preventDefault();
let parent = e.target;
let ul = document.createElement('ul');
let li = document.createElement('li');
ul.appendChild(li);
parent.appendChild(ul);
moveCursorToEnd(li);
} else if (code == ENTER_KEY) {
e.preventDefault(); // Stop treating it like an actual line feed
let parent = e.target;
let li = document.createElement('li');
parent.appendChild(li);
moveCursorToEnd(li);
}
});
function moveCursorToEnd(el) {
el.focus();
document.execCommand('selectAll', false, null);
document.getSelection().collapseToEnd();
}
I commented out all of your main code and put an alert in the event listener to find out what was going on. It turns out it was sending the error cannot use property addEventListener of undefined as it did not like window.editor.
The solution was to replace window.editor with just editor.
Try typing in your contenteditable div in the snippet below to see how it works when you use editor instead. This is probably because you declared the editor variable with let instead of var, which affects the scope of the variable in a different way so it does not become a property of the window object.
const TAB_KEY = 9;
const ENTER_KEY = 13;
let editor = document.querySelector('.editor');
editor.appendChild(document.createElement('li')); // Add initial (for visibility)
/*editor.addEventListener('focus', (e) => {
//let ul = e.target;
//let li = document.createElement('li');
//ul.appendChild(li);
});*/
editor.addEventListener('keydown', (e) => {
/*let code = e.keyCode || e.which;
if (code == TAB_KEY) {
e.preventDefault();
let parent = e.target;
let ul = document.createElement('ul');
let li = document.createElement('li');
ul.appendChild(li);
parent.appendChild(ul);
moveCursorToEnd(li);
} else if (code == ENTER_KEY) {
e.preventDefault(); // Stop treating it like an actual line feed
let parent = e.target;
let li = document.createElement('li');
parent.appendChild(li);
moveCursorToEnd(li);
}*/
alert("Works");
});
<div>
<input type="button" onclick="reset();" value="Reset form">
<button id="pr" onclick="reset()"> Clear </button>
<div id="ul" class="editor" contenteditable="true" draggable="true" ></div>
</div>
I want to add a delete button beside each of the items that are to be added.
How to do this properly so the all the functions work?
I have tried the method below as you will see in the code. This seems correct to me but it's not working. This needs to be purely JavaScript`.
var button = document.createElement("BUTTON");
var ul = document.getElementById("list");
var li = document.createElement("li");
function handleAddNewItem() //adds new items and more
{
var item = document.getElementById("input").value;
var ul = document.getElementById("list");
var li = document.createElement("li");
if (item === '') {
alert("Input field can not be empty");
}
else {
button.innerText = "Delete";
li.appendChild(document.createTextNode("- " + item));
ul.appendChild(li);
ul.appendChild(button);
}
document.getElementById("input").value = ""; //clears input
//li.onclick = clearDom;
}//code deletes items by clearDom function
document.body.onkeyup = function (e) //allows items to be added with enter button
{
if (e.keyCode == 13) {
handleAddNewItem();
}
}
function clearDom() {
//e.target.parentElement.removeChild(e.target);//removeChild used
ul.removeChild(li);
ul.removeChild(button);
}
button.addEventListener("click", clearDom);
<body>
<input id="input" placeholder="What needs to be done?">
<button id="add_button" onclick="handleAddNewItem()">ADD</button>
<ul id="list">
</ul>
</body>
<script src="new.js"></script>
</html>
var button = document.createElement("BUTTON");
var ul = document.getElementById("list");
var li = document.createElement("li");
function handleAddNewItem() //adds new items and more
{
var item = document.getElementById("input").value;
var ul = document.getElementById("list");
var li = document.createElement("li");
if (item === '') {
alert("Input field can not be empty");
} else {
button.innerText = "Delete";
li.appendChild(document.createTextNode("- " + item));
ul.appendChild(li);
ul.appendChild(button);
}
document.getElementById("input").value = ""; //clears input
//li.onclick = clearDom;
} //code deletes items by clearDom function
document.body.onkeyup = function(e) //allows items to be added with enter button
{
if (e.keyCode == 13) {
handleAddNewItem();
}
}
function clearDom() {
//e.target.parentElement.removeChild(e.target);//removeChild used
ul.removeChild(li);
ul.removeChild(button);
}
button.addEventListener("click", clearDom);
<input id="input" placeholder="What needs to be done?">
<button id="add_button" onclick="handleAddNewItem()">ADD</button>
<ul id="list">
</ul>
<!-- commented out to reduce errors in the console
<script src="new.js"></script> -->
I am facing this error for now-
"The node to be removed is not a child of this node. at
HTMLButtonElement.clearDom new.js:33:7"
I want to implement the delete button in line with the items listed. so that it deletes the items added one by one separately.
I'd suggest:
function handleAddNewItem() {
/* Move the creation of all variables within the function
in which they're being used: */
const button = document.createElement('button'),
ul = document.getElementById('list'),
li = document.createElement('li'),
item = document.getElementById('input').value;
// here we use String.prototype.trim() to remove leading
// and trailing whitespace from the entered value, to
// prevent a string of white-space (' ') being considered
// valid:
if (item.trim() === '') {
alert("Input field can not be empty");
} else {
button.textContent = "Delete";
// here we again use String.prototype.trim(), this time to
// avoid the creation of a ' task '
// with extraneous white-space:
li.appendChild(document.createTextNode("- " + item.trim()));
// appending the <button> to the <li> instead
// of the <ul> (of which it would be an invalid
// child element anyway):
li.appendChild(button);
ul.appendChild(li);
}
document.getElementById("input").value = ''; //clears input
}
document.body.onkeyup = function(e) //allows items to be added with enter button
{
if (e.keyCode == 13) {
handleAddNewItem();
}
}
// the e - the EventObject - is passed automagically from
// the later use of EventTarget.addEventListener():
function clearDom(e) {
// e.target is the element on which the event that we're
// reacting to was originally fired (the <button>):
const clickedButton = e.target;
// here we use DOM traversal methods to find the closest
// ancestor <li> element, and then use ChildNode.remove()
// to remove it from the DOM:
clickedButton.closest('li').remove();
}
// using event-delegation to catch the
// delete-button clicks:
// first we retrieve the element already on the page which
// will be an ancestor of the appended elements:
document.getElementById('list')
// we then bind the clearDom() function - note the deliberate
// lack of parentheses - as the 'click' event-handler:
.addEventListener('click', clearDom);
function handleAddNewItem() {
/* Creating all variables within the function: */
const button = document.createElement('button'),
ul = document.getElementById('list'),
li = document.createElement('li'),
item = document.getElementById('input').value;
if (item.trim() === '') {
alert("Input field can not be empty");
} else {
button.textContent = "Delete";
li.appendChild(document.createTextNode("- " + item));
li.appendChild(button);
ul.appendChild(li);
}
document.getElementById("input").value = '';
}
document.body.onkeyup = function(e) {
if (e.keyCode == 13) {
handleAddNewItem();
}
}
function clearDom(e) {
const clickedButton = e.target;
clickedButton.closest('li').remove();
}
document.getElementById('list')
.addEventListener('click', clearDom);
<input id="input" placeholder="What needs to be done?">
<button id="add_button" onclick="handleAddNewItem()">ADD</button>
<ul id="list">
</ul>
While this question is already, arguably, already answered, I had a few moments to spare and took advantage of this question to begin learning how to use custom elements. The code, as above, is explained so far as possible using comments in the code itself:
// using an Immediately-Invoked Function
// Expression ('IIFE') to handle the creation of the
// custom element:
(function() {
// creating an HTML <template> element, this could
// instead be placed in, and retrieved from, the DOM:
const template = document.createElement('template');
// using a template literal to create, and format
// the HTML of the created <template> (using a template
// literal allows for new-lines and indentation):
template.innerHTML = `
<style>
*, ::before, ::after {
padding: 0;
margin: 0;
box-sizing: border-box;
}
div.layout {
display: grid;
grid-template-columns: 1fr min-content;
}
div.buttonWrap {
display: flex;
flex-direction: column;
align-items: flex-start;
}
</style>
<div class="layout">
<p></p>
<div class="buttonWrap">
<button>delete</button>
</div>
</div>
`;
// using class syntax:
class TaskItem extends HTMLElement {
// the constructor for the class and, by extension,
// the element that we're defining/creating:
constructor() {
// it seems that super() must be placed as the
// first thing in the constructor function:
super();
// we're holding the contents of the custom
// element in the Shadow DOM, to avoid its
// descendants being affected by CSS in the
// parent page and to prevent JavaScript in
// the document from interacting with the
// contents:
this.attachShadow({
// we want to interact and use elements in
// the Shadow Root, so it must be 'open'
// (although 'closed' is the other valid
// mode-type:
mode: 'open'
});
// here we append the content - not the node
// itself - of the created <template> element
// using Node.cloneNode(), the Boolean true
// means that the descendant elements are also
// cloned and therefore appended:
this.shadowRoot.appendChild(
template.content.cloneNode(true)
);
// for easier reading we cache the shadowRoot
// here (otherwise line-lengths can be a bit
// silly):
const root = this.shadowRoot,
// retrieving the <button> element, which will
// handle the task deletion:
del = root.querySelector('button');
// binding the anonymous function - defined
// using an Arrow function as we don't
// want to change the 'this' in the function -
// as the event-handler for the 'click' event:
del.addEventListener('click', () =>
// here we traverse to the parentNode of
// the 'this', and then use
// parentNode.removeChild() to remove the
// 'this' node:
this.parentNode.removeChild(this));
}
// this callback is executed when the element is
// connected/attached to the DOM:
connectedCallback() {
// we find the Shadow Root:
this.shadowRoot
// find the descendent <p> element:
.querySelector('p')
// and set its text-content to be equal
// to that of the data-task attribute:
.textContent = this.dataset.task;
}
}
// here we define the custom element and its
// class:
window.customElements.define('task-item', TaskItem);
})();
// here we cache a reference to the <button> which will
// cause the addition of new tasks:
const addTask = document.getElementById('add_button'),
// define the function that will handle the
// addition of new tasks:
createTask = () => {
// caching the <input> element:
const taskSource = document.getElementById('input'),
// retrieving and trimming the entered
// <input> value:
task = taskSource.value.trim(),
// creating a new element (custom
// elements are created the same way
// as 'normal' elements):
createdTask = document.createElement('task-item');
// updating the data-task attribute, for
// retrieval/use later when the element
// is added to the DOM:
createdTask.dataset.task = task;
// if we have a task (a zero-length/empty
// string is considered falsey, a string
// with a length greater than zero is
// considered truthy and string with negative
// length is considered impossible (I think),
// and therefore falsey:
if (task) {
// we retrieve the element holding the
// <task-item> elements:
document.getElementById('list')
// and append the created element:
.appendChild(createdTask);
}
// removing the <input> element's value:
taskSource.value = '';
};
// adding createTask() as the event-handler for
// the 'click' event on the <button>:
addTask.addEventListener('click', createTask);
// binding an anonymous function as the handler for
// keyup events on the <body> (binding to a closer
// ancestor would be more sensible in production):
document.body.addEventListener('keyup', (e) => {
// if the e.which is 13 we trust that to be the
// enter key, and then we call createTask()
if (e.which === 13) {
createTask();
}
})
#list {
margin-top: 0.5em;
min-height: 1.5em;
background: transparent radial-gradient(at 0 0, skyblue, lime);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-gap: 5px;
}
#list:empty::before {
content: 'Add a new task!';
background: transparent linear-gradient(to right, #fffa, #fff0);
padding: 0 0 0 1em;
}
task-item {
border: 2px solid lime;
padding: 0.25em;
background-color: #fff9;
}
<input id="input" class="add_task" placeholder="What needs to be done?">
<button id="add_button" class="add_task">ADD</button>
<div id="list"></div>
JS Fiddle demo.
ChildNode.remove().
Classes.
Constructor.
document.createElement().
document.getElementById().
document.querySelector().
Element.attachShadow().
Event object.
event.target.
EventTarget.addEventListener().
Node.appendChild().
Node.parentNode.
Node.removeChild().
Node.textContent.
super().
Window.customElements.
Not very nice but a solution :)
else {
button.innerText = 'Delete';
li.appendChild(document.createTextNode('- ' + item));
ul.appendChild(li);
let but = button.cloneNode(true); // <-- solution
li.appendChild(but);
// clearDom function
clearDom();
}
And also a function that erases a single entry
function clearDom() {
let buttons = document.querySelectorAll('button:not(#add_button)');
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function (e) {
e.target.parentNode.remove();
}, false);
}
}
let button = document.createElement('button');
let ul = document.getElementById('list');
let li = document.createElement('li');
function handleAddNewItem() {
let item = document.getElementById('input').value;
let ul = document.getElementById('list');
let li = document.createElement('li');
if (item === '') {
alert('Input field can not be empty');
}
else {
button.innerText = 'Delete';
li.appendChild(document.createTextNode('- ' + item));
ul.appendChild(li);
let but = button.cloneNode(true);
li.appendChild(but);
clearDom();
}
document.getElementById('input').value = ''; // clears input
}
function clearDom() {
let buttons = document.querySelectorAll('button:not(#add_button)');
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function (e) {
e.target.parentNode.remove();
}, false);
}
}
document.body.onkeyup = function (e) {
if (e.keyCode === 13) {
handleAddNewItem();
}
};
<input id="input" placeholder="What needs to be done?">
<button id="add_button" onclick="handleAddNewItem()">ADD</button>
<ul id="list"></ul>