TO DO LIST in javascript - javascript

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>

Related

List element deleted after li is toggle

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>

ClassList toggle works only for some elements

I am an absolute beginner in this field. And what i m doing is a small todo-list page.
And I am stuck in the following 'button events' part.
As in the code, I want to toggle the class of 'completed' to the ancestor div of the button clicked. However, it seems to work only for some elements.
Meaning, if the nodelist of 'finishBtn' variable has an odd number length, the class list only toggles for even number indexes and vice versa.
For example, if nodelist.length = 3, then the class list only toggles for nodelist[0]
and nodelist[2].
(However, for removeBtn variable, it works just fine)
Thank you very much for your time. I would really appreciate every reply of yours.
Cos I m stuck in this bloody thing for hours.
addBtn.addEventListener('click', (e)=> {
e.preventDefault();
if(input.value === ''){
return;
}
// adding elements to the body;
const eachTodo = document.createElement('div');
eachTodo.classList.add('eachTodo');
const textName = document.createElement('p');
textName.textContent = input.value;
const btns = document.createElement('div');
btns.classList.add('btns');
const finish = document.createElement('button');
finish.classList.add('finish');
finish.textContent = 'Finish';
const remove = document.createElement('button');
remove.classList.add('remove');
remove.textContent = 'Remove';
btns.append(finish, remove);
eachTodo.append(textName, btns);
plansDiv.append(eachTodo);
input.value = '';
//button Events
const finishBtn = document.querySelectorAll('.finish');
const removeBtn = document.querySelectorAll('.remove');
finishBtn.forEach(btn => {
btn.addEventListener('click', ()=>{
btn.parentElement.parentElement.classList.toggle('completed');
})
})
removeBtn.forEach(btn => {
btn.addEventListener('click', ()=>{
btn.parentElement.parentElement.remove();
})
})
})
This is my CSS part
.completed p {
text-decoration: line-through;
opacity: 0.8;
}
As it stands you're querying all buttons on each add and adding new listeners to them all resulting in duplicate listeners on each button that fire sequentially.
Elements with an even number of listeners attached will toggle the class an even number of times and return to the initial value.
"on" toggle-> "off" toggle-> "on"
Elements with an odd number of attached listeners toggle the class an odd number of times and appear correct at the end.
"on" toggle-> "off" toggle-> "on" toggle-> "off"
(It works for Remove because the first listener removes the element and subsequent Removes don't change that.)
Add listeners only once to each button
You can avoid this by simply adding the listeners directly to the newly created buttons. You already have references to each button and their parent element (eachTodo) in the script, so you can just add the listeners directly to them and reference the parent directly.
finish.addEventListener('click', () => {
eachTodo.classList.toggle('completed');
});
remove.addEventListener('click', () => {
eachTodo.remove();
});
const addBtn = document.getElementById('addBtn');
const plansDiv = document.getElementById('plansDiv');
const input = document.getElementById('input');
addBtn.addEventListener('click', (e) => {
e.preventDefault();
if (input.value === '') {
return;
}
// adding elements to the body;
const eachTodo = document.createElement('div');
eachTodo.classList.add('eachTodo');
const textName = document.createElement('p');
textName.textContent = input.value;
const btns = document.createElement('div');
btns.classList.add('btns');
const finish = document.createElement('button');
finish.classList.add('finish');
finish.textContent = 'Finish';
const remove = document.createElement('button');
remove.classList.add('remove');
remove.textContent = 'Remove';
btns.append(finish, remove);
eachTodo.append(textName, btns);
plansDiv.append(eachTodo);
input.value = '';
// Add listeners directly
finish.addEventListener('click', () => {
eachTodo.classList.toggle('completed');
})
remove.addEventListener('click', () => {
eachTodo.remove();
})
})
.completed p {
text-decoration: line-through;
opacity: 0.8;
}
<input type="text" id="input">
<button type="button" id="addBtn">Add</button>
<div id="plansDiv"></div>
Event delegation
A more concise solution would be to use event delegation and handle all the buttons in a single handler added to the document. Here replacing parentElement with closest('.eachTodo') to avoid the fragility of a specific ancestor depth, and checking which button was clicked using Element.matches().
document.addEventListener('click', (e) => {
if (e.target.matches('button.finish')){
e.target.closest('.eachTodo').classList.toggle('completed');
}
if (e.target.matches('button.remove')){
e.target.closest('.eachTodo').remove();
}
});
const addBtn = document.getElementById('addBtn');
const plansDiv = document.getElementById('plansDiv');
const input = document.getElementById('input');
document.addEventListener('click', (e) => {
if (e.target.matches('button.finish')){
e.target.closest('.eachTodo').classList.toggle('completed');
}
if (e.target.matches('button.remove')){
e.target.closest('.eachTodo').remove();
}
});
addBtn.addEventListener('click', (e) => {
if (input.value === '') {
return;
}
// adding elements to the body;
const eachTodo = document.createElement('div');
eachTodo.classList.add('eachTodo');
const textName = document.createElement('p');
textName.textContent = input.value;
const btns = document.createElement('div');
btns.classList.add('btns');
const finish = document.createElement('button');
finish.classList.add('finish');
finish.textContent = 'Finish';
finish.type = 'button';
const remove = document.createElement('button');
remove.classList.add('remove');
remove.textContent = 'Remove';
remove.type = 'button';
btns.append(finish, remove);
eachTodo.append(textName, btns);
plansDiv.append(eachTodo);
input.value = '';
});
.completed p {
text-decoration: line-through;
opacity: 0.8;
}
<input type="text" id="input">
<button type="button" id="addBtn">Add</button>
<div id="plansDiv"></div>
Note that you can also avoid the necessity of e.preventDefault() by specifying type="button" when you create your buttons. see: The Button element: type

Foreach element create one element after it on click (error warning)

So for a form I'd like to create an error message after each element which is invalid, onclick of the submit button.
I have almost got it right, except it adds the span twice after the element (because two elements are invalid). I need to to add it once, to both invalid elements.
So my JS/jquery (yes, I know, I mixed it :p):
function checkrequired() {
var nbform = document.getElementById("userpanel");
var elements = document.querySelectorAll('[required]');
elements.forEach(element => {
if (element.value === "") {
nbform.classList.add('submitted-form-invalid');
$('.show-error').after("<span class='col-sm-12' style='color:red;'> Name is required.</span>");
} else {
return true;
}
});
How it looks:
I also know it adds it twice, instead of once, because it adds it foreach element. But I don't know how to surpass this. Anyone?
EDIT:
I came up with the next Javascript, this idea is all that I need. It doesn't need to be really complicated:
function checkrequired() {
var el = document.createElement("div");
el.setAttribute('class', 'error-message');
el.innerHTML = "Dit veld is verplicht";
var x = document.querySelectorAll('.show-error');
for (var i = 0; i < x.length; i++) {
x[i].appendChild(el);
}
}
The only problem now is that it only appends the child to the sconds div with class "error-message". Not both.
try this
// select your inputs with an id
var elements = document.querySelectorAll('#voornaam');
$("button").click(function(){
elements.forEach(element => {
if (element.value === ""&& element.nextSibling.id !="error") {
$("<span class='col-sm-12 error' id='error' style='color:red;'> Name is required.</span>").insertAfter(element);
return
}
else return true
})
});
});
$('.show-error') returns an array of elements. For all these elements you are adding the span after it checks invalid. I think plain js gives you more control here.
So here's an example snippet in plain vanilla js.
Maybe this jsFiddle is helpfull too. And remember: checking in the browser will not render a server side check unnecessary.
document.addEventListener("click", checkRequired);
const setError = (elem, remove) => {
const span = document.createElement("span");
span.classList.add("foutmelding");
span.appendChild(document.createTextNode(` vul svp ${elem.getAttribute("placeholder")} in`));
elem.insertAdjacentElement("afterEnd", span);
};
function checkRequired(evt) {
if (evt.target.id === "check") {
document.querySelectorAll(".foutmelding")
.forEach(el => el.parentNode.removeChild(el));
const validMsg = document.querySelector("#formValidation");
validMsg.dataset.invalid = "";
document.querySelectorAll('[required]')
.forEach(element => {
if (element.value.trim() === "") {
setError(element);
validMsg.dataset.invalid = "onvolledig ingevuld";
}
});
validMsg.dataset.invalid = validMsg.dataset.invalid || "all checks out well";
}
}
input {
margin: 0.3rem 0;
}
.foutmelding,
.formulierMelding {
color: red;
}
.formulierMelding:before {
content: attr(data-invalid);
}
<div id="theForm">
<input type="text" id="voornaam" placeholder="voornaam" required><br>
<input type="text" id="email" placeholder="e-mailadres" required>
</div>
<button id="check">Check</button>
<span id="formValidation" data-invalid class="formulierMelding"></span>

Modify dynamically generated li elements with jQuery when dblclicked

I have a simple To Do list that when a new ToDo is generated it creates and appends a new li element into a preexisting ul. I have a toggleAll()function that adds a class which strikes through the li elements when double clicked. What I want to do now is to create a method that makes it so when I double click an li element, it toggles the line-through class just for the element clicked, so I can mark individual ToDos as completed. I tried using regular JS and jQuery but couldn't find the way to do it with either.
My ToDos are composed of a text property and a boolean determining whether the task is completed or not. Toggling sets the boolean to true, and if the boolean is true, then the .marked class is added to the content of the li element.
addTodo: function(todo){
this.list.push({
todoText: todo,
completed: false,
});
console.log(this.list);
},
var view = {
display: function(){
var printed = document.getElementById('printedList');
printed.innerHTML = '';
for(var i = 0; i < todos.list.length; i++){
var newLi = document.createElement('li');
newLi.innerHTML = todos.list[i].todoText;
printed.appendChild(newLi);
}
}
}
.marked{
text-decoration: line-through;
}
<div id='listica'>
<ul id='printedList'></ul>
</div>
Edit: toggleAll function:
toggleAll: function(){
var printedElements = document.getElementById('printedList');
for(var i = 0; i < todos.list.length; i++){
if(todos.list[i].completed == false){
todos.list[i].completed = true;
printedElements.style.textDecoration = 'line-through';
} else if(todos.list[i].completed == true){
todos.list[i].completed = false;
printedElements.style.textDecoration = '';
}
};
You can use .addEventListener() or jQuery's .on().
With .addEventListener you need to loop through all of the lis:
const lis = document.querySelectorAll('li');
lis.foreach(function(li) {
li.addEventListener('dblclick', function(e) {
e.target.classList.add('line-through');
});
});
For .on() you don't have to loop through the elements.
$('li').on('dblclick', function(e) {
$(e.target).addClass('line-through');
});
.on()
.addEventListener()
event.target
NodeList.forEach
Element.classList

JavaScript - addEventListener on all created li elements

So I have a simple script that adds "li" elements to the "ul" and assigns them a class. Now I want to change the class of "li" item on click event.
Here is the HTML:
<form class="form">
<input id="newInput" type="text" placeholder="Dodaj pozycję">
<button id="createNew" type="button">Dodaj</button>
</form>
<h2>Moja lista:</h2>
<div class="listBg">
<ul id="list">
</ul>
</div>
<button id="deleteAll" type="button">Wyczyść</button>
And JS:
function addItem() {
var myList = document.getElementById("list"); // get the main list ("ul")
var newListItem = document.createElement("li"); //create a new "li" element
var itemText = document.getElementById("newInput").value; //read the input value from #newInput
var listText = document.createTextNode(itemText); //create text node with calue from input
newListItem.appendChild(listText); //add text node to new "li" element
if (itemText === "") { // if input calue is empty
alert("Pole nie może być puste"); // show this alert
} else { // if it's not empty
var x = document.createElement("span"); // create a new "span" element
x.innerText = "X"; // add inner text to "span" element
x.className = "closer"; // add class to "span" element
myList.appendChild(newListItem); // add created "li" element to "ul"
newListItem.className = "item"; // add class to new "li" element
newListItem.appendChild(x); // add a "span" to new "li" element
var itemText = document.getElementById("newInput"); // read current input value
itemText.value = ""; // set current input calue to null
}
};
I was thinking something like this should do the trick, but it's not working:
function itemDone() {
var listItems = document.querySelectorAll("li");
var i;
for (i = 0; i < listItems.length; i++) {
listItem[i].className = "itemDone";
};
};
var item = document.getElementsByClassName("item");
item.addEventListener("click", itemDone);
I'm fairly new to javascript so I would appreciate some explanation with the answer.
Use event delegation for the dynamically created elements. With this, you only need one event listener on the ul#list and it will work for all elements you dynamically attach to it:
document.getElementById("list").addEventListener("click",function(e) {
if (e.target && e.target.matches("li.item")) {
e.target.className = "foo"; // new class name here
}
});
Here's a simplified example so you can see what happens with the code:
function addItem(i) {
var li = document.createElement('li');
li.appendChild(document.createTextNode(i));
li.className = 'item';
document.getElementById('list').appendChild(li);
}
var counter = 2;
document.getElementById('btn').addEventListener('click', function() {
addItem(counter++);
});
document.getElementById("list").addEventListener("click", function(e) {
if (e.target && e.target.matches("li.item")) {
e.target.className = "foo"; // new class name here
alert("clicked " + e.target.innerText);
}
});
<ul id="list">
<li class="item">1</li>
</ul>
<button id="btn">
add item
</button>
You'll have to set the eventListener on each single item, as document.getElementsByClassName() returns a collection of items and you can't simply add an event listener to all of them with one call of addEventListener().
So, just like the loop you used in itemDone(), you'll have to iterate over all items and add the listener to them:
var items = document.getElementsByClassName("item");
for (var i = 0; i < items.length; i++) {
items[i].addEventListener("click", itemDone);
}
As pointed out in the comments, you can also do so directly when creating the elements, so in your addItem() function, add:
newListItem.addEventListener("click", itemDone);
try this :
var item = document.getElementsByClassName("item");
for (var i = 0; i < item.length; i++) {
item[i].addEventListener("click", itemDone);
}
function addItem(i) {
var li = document.createElement('li');
li.appendChild(document.createTextNode(i));
li.className = 'item';
document.getElementById('list').appendChild(li);
}
var counter = 2;
document.getElementById('btn').addEventListener('click', function() {
addItem(counter++);
});
document.getElementById("list").addEventListener("click", function(e) {
if (e.target && e.target.matches("li.item")) {
e.target.className = "foo"; // new class name here
alert("clicked " + e.target.innerText);
}
});
<ul id="list">
<li class="item">1</li>
<li class="item">1</li>
<li class="item">1</li>
<li class="item">1</li>
<li class="item">1</li>
</ul>
<button id="btn">
add item
</button>
First, try using getElementByTagName instead of querySelectorAll, because querySelectorAll is slower. And second, item receives an array, so item.addEventListener will give you an error. You have to do the addEventListener over item[counter], in a loop.
document.getElementById("list").addEventListener("click", function (e) {
const parent = e.target.parentElement;
const siblings = Array.from(parent.getElementsByTagName("LI"));
siblings.forEach(n => (n.className = (e.target === n) ? "foo" : ""));
});

Categories

Resources