Add task to tasklist from JS to HTML page - javascript

I am trying to input a new task. It will only allow me to add one task. If I input another it just removes the last. How can I save multiple?
HTML Code:
<h2>Future Projects</h2>
<div id="projects">
<div class="project">first</div>
<div class="project">y</div>
<div class="project">last</div>
</div>
<form action="index.html" get>
<label for="message">Meaningful Message:</label>
<br>
<textarea name="message" id="message" rows="1" cols="20">
</textarea>
<br>
<input type="submit" value="submit">
</form>
</div>
JS Code:
const message = words.get('message');
if(message.value !== '') {
let e = document.createElement('div')
e.innerHTML = `${message}`
e.className = 'project'
let parent = document.getElementById('projects')
parent.append(e);
e.preventDefault()
}

Try apppendChild.
const project = document.createElement("div");
project.innerText = "text";
//...
const parent = document.getElementById('projects');
parent.appendChild(project);

I was able to get it !
JS Changes:
let submit = document.getElementById('submit').addEventListener('click', add)
function add(event){
event.preventDefault()
let message = document.getElementById('message').value
let parent = document.getElementById('projects')
parent.innerHTML += `<div class="project">${message}</div>`
}
and
let submit = document.getElementById('submit').addEventListener('click', add)
function add(event){
event.preventDefault()
let message = document.getElementById('message').value
let parent = document.getElementById('projects')
let e = document.createElement('div')
e.innerHTML = `${message}`
e.className = 'project'
parent.appendChild(e);
}

Related

Why VS Code autocomplete doesn't suggest .value attribute when working with Java Script file?

HTML:
<body>
<h1>Grocery List</h1>
<form action="/nowhere">
<label for="item">Enter A Product</label>
<input type="text" id="product" name="product" />
<label for="item">Enter A Quantity</label>
<input type="number" id="qty" name="qty" />
<button>Submit</button>
</form>
<ul id="list"></ul>
</body>
Java Script:
const frm = document.querySelector("form");
const prdct = document.querySelector("#product");
const qty = document.querySelector("#qty");
const mylst = document.querySelector("#list");
frm.addEventListener("submit", (e) => {
e.preventDefault();
const myele = document.createElement("li");
myele.textContent = qty.value;
myele.textContent += ` ${prdct.value}`;
mylst.appendChild(myele);
qty.value = "";
prdct.value = "";
});
As can be seen, VS code isn't suggesting '.value' attribute with other suggestions. What can be the reason?
Document.querySelector() returns a Element, so the editor cannot know that it has a value.
If you would create the element by javascript, then the editor DOES know...
let input = document.createElement("input")
let test = input.value // now you can get autocomplete
Another way is to use typescript :
let input:HTMLInputElement = document.querySelector("myinput")!
let test = input.value // now you can get autocomplete

How to create a delete function for vanillajs todo list app?

Good day! I've been trying to figure out how to create a delete function in my todo app. I dont know what to do next and the delete function as well as the eventListener is not correct. I hope you guys can help me. I want to fully understand every small projects that I make. Thank you in advance! :)
const inputBox = document.querySelector('.input')
const addBtn = document.querySelector('.input-button')
const todoMain = document.querySelector('.todo-list')
const deleteBtn = document.querySelector('.delete-button')
const deleteAllBtn = document.querySelector('.clear-all')
//Event listeners
inputBox.addEventListener("keyup", function(){
let userInput = inputBox.value;
if (userInput.trim() != 0) {
addBtn.classList.add("active")
} else {
addBtn.classList.remove("active");
}
})
addBtn.addEventListener("click", todoAdd);
todoMain.addEventListener("click", todoDelete);
// Functions
function todoAdd(event){
event.preventDefault();
const todoLi = document.createElement('li');
todoLi.innerText = inputBox.value;
const todoDeleteBtn = document.createElement('button');
todoDeleteBtn.innerHTML = `<i class="fas fa-trash-alt"></i>`;
todoDeleteBtn.classList.add('delete-button')
todoLi.appendChild(todoDeleteBtn);
todoMain.appendChild(todoLi);
inputBox.value = '';
addBtn.classList.remove("active");
};
function todoDelete(e){
const item = e.target;
if (item.classList[0] === 'delete-button'){
todoMain.removeChild(todoLi);
}
}
<link crossorigin rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />
<div class="container">
<h1>TODO list</h1>
<div class="input-container">
<input type="text" class="input" placeholder="Input Text Here">
<button class="input-button"><i class="fas fa-plus"></i></button>
</div>
<ul class="todo-list">
</ul>
<div class="footer">
<span>You have<span class="pending">0</span>pending task left</span>
<button class="clear-all">Clear All</button>
</div>
</div>
I have implemented it quite simply as an example.
When creating the ToDo item, I add a key for the text and a data attribute for the delete button and an onclick event.
The key is important to have the relation between button and text. First i used new Date() but i updated with an random Method. (Math.random()+1).toString().split('.')[1];
For deletAll() you can get the entire Parent Node and set html to an empty string.
const inputBox = document.querySelector('.input')
const addBtn = document.querySelector('.input-button')
const todoMain = document.querySelector('.todo-list')
const deleteBtn = document.querySelector('.delete-button')
const deleteAllBtn = document.querySelector('.clear-all')
//Event listeners
inputBox.addEventListener("keyup", function(){
let userInput = inputBox.value;
if (userInput.trim() != 0) {
addBtn.classList.add("active")
} else {
addBtn.classList.remove("active");
}
})
addBtn.addEventListener("click", todoAdd);
todoMain.addEventListener("click", todoDelete);
// Functions
function todoAdd(event){
event.preventDefault();
const todoLi = document.createElement('li');
const key =(Math.random()+1).toString().split('.')[1];
todoLi.innerText = inputBox.value;
todoLi.setAttribute("id", key);
const todoDeleteBtn = document.createElement('button');
todoDeleteBtn.innerHTML = `<i class="fas fa-trash-alt"></i>`;
todoDeleteBtn.classList.add('delete-button')
todoDeleteBtn.onclick = function() {
const _key = this.getAttribute('data-key')
document.getElementById(_key).remove();
this.remove()
}
todoDeleteBtn.setAttribute("data-key", key);
todoLi.appendChild(todoDeleteBtn);
todoMain.appendChild(todoLi);
inputBox.value = '';
addBtn.classList.remove("active");
};
function todoDelete(e){
const item = e.target;
if (item.classList[0] === 'delete-button'){
todoMain.removeChild(todoLi);
}
}
<link crossorigin rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />
<div class="container">
<h1>TODO list</h1>
<div class="input-container">
<input type="text" class="input" placeholder="Input Text Here">
<button class="input-button"><i class="fas fa-plus"></i></button>
</div>
<ul class="todo-list">
</ul>
<div class="footer">
<span>You have<span class="pending">0</span>pending task left</span>
<button class="clear-all">Clear All</button>
</div>
</div>
There are a couple of issues that might be stopping this from working:
Your function todoDelete tries to access the variable todoLi, which doesn't exist in its scope. You define this inside todoAdd, but its scope is limited so you can't access the variable from outside the function.
I suspect what you might want to be doing is passing item instead.
You attach the event listener that triggers todoDelete to your todoMain element, which means that e.target for the function will always be the ul element, not your list element. Your if is then always false so the code never runs.
To fix this, attach the event listener to the todoDeleteBtn in your todoAdd function instead.

How can I get my JavaScript delete function to fire more than once?

I'm building a todo app and I use a function to create a list item entered by the user.
There is an event listener added to the output section to listen for a delete button click for each item displayed. My problem is that the delete button is only working for one item and then it stops working.
In the console, it appears that the function is actually called every time I press the button, but the functionality only works for one click. Do I need to add all the list items into an array perhaps?
const todo = document.getElementById('todo');
const enter = document.getElementById('enter');
const output = document.getElementById('output');
enter.addEventListener('click', () => {
listItem(todo);
});
let createListItem;
var deleteBtn;
let checkBtn;
function listItem(todo) {
createListItem = document.createElement('li');
createListItem.innerText = todo.value;
todo.value = '';
output.appendChild(createListItem);
checkBtn = document.createElement('button');
deleteBtn = document.createElement('button');
checkBtn.innerText = 'check';
deleteBtn.innerText = 'delete';
createListItem.append(checkBtn);
createListItem.append(deleteBtn);
checkBtn.classList.add('checkBtn');
deleteBtn.classList.add('deleteBtn');
}
output.addEventListener('click', deleteFunc);
function deleteFunc() {
console.log('function called');
createListItem.remove();
}
<section class="controls">
<div>
<label for="todo">Enter a to-do</label>
<input type="text" name="todo" id="todo">
</div>
<span>
<button id="enter" class = "enter"><i class="fas fa-paper-plane"></i></button>
</span>
</section>
<section>
<ul id="output" class="output">
</ul>
</section>
You need to delegate and use relative addressing because your code only removes the LAST added LI
The variable createListItem pollutes the global scope. Add the keyword var or let in the listItem function too
function deleteFunc(e) {
console.log('function called');
const tgt = e.target;
if (e.target.innerText==="delete") tgt.closest("li").remove()
}
Added benefit from this delegation is that adding the functionality to the "check" button is just
if (e.target.innerText==="check") ...
I would recommend to use a class and testing
if (e.target.classList.contains("delete")
instead of the innerText - especially if you want to change language of the button
const todo = document.getElementById('todo');
const enter = document.getElementById('enter');
const output = document.getElementById('output');
enter.addEventListener('click', () => {
listItem(todo);
});
let createListItem;
var deleteBtn;
let checkBtn;
function listItem(todo) {
let createListItem = document.createElement('li'); // use let or var here
createListItem.innerText = todo.value;
todo.value = '';
output.appendChild(createListItem);
checkBtn = document.createElement('button');
deleteBtn = document.createElement('button');
checkBtn.innerText = 'check';
deleteBtn.innerText = 'delete';
createListItem.append(checkBtn);
createListItem.append(deleteBtn);
checkBtn.classList.add('checkBtn');
deleteBtn.classList.add('deleteBtn');
}
output.addEventListener('click', deleteFunc);
function deleteFunc(e) {
console.log('function called');
const tgt = e.target;
if (e.target.innerText==="delete") tgt.closest("li").remove()
}
<section class="controls">
<div>
<label for="todo">Enter a to-do</label>
<input type="text" name="todo" id="todo">
</div>
<span>
<button id="enter" class = "enter"><i class="fas fa-paper-plane"></i></button>
</span>
</section>
<section>
<ul id="output" class="output">
</ul>
</section>
Your createListItem variable is a global that gets set to the most-recently appended item, so the delete function will always delete the most-recent item. Once an element el has already been removed from the DOM tree, el.remove() is a no-op, so it only works once.
To fix, you can either use event delegation as in #mplungjan's answer or assign a unique id to each list item and pass that as a parameter to the function to determine what to delete.
Example of the second approach:
<ul>
<li id="item-0"></li>
<li id="item-1"></li>
<li id="item-2"></li>
</ul>
const deleteById = id => document.querySelector(`#item-${id}`).remove()
First though:
I think os because you are targeting the "ul" instead the "li" elements. Try adding the event listeners to each "li" element with a querySelectorAll() instead of targeting "output" directly.
When you add another item, you call listItem() which sets a new value to createListItem. This means, when you call deleteFunc(), createListItem.remove(); is only executed on the last element you have added. If this item was already removed, your out of luck as well.
function deleteFunc(e) {
console.log('function called');
const { target } = e; // get target
target.closest("li").remove()
}
Remove the listener on output. (This line: output.addEventListener('click', deleteFunc);)
And add the listener to each deleteBtn:
checkBtn.classList.add('checkBtn');
deleteBtn.classList.add('deleteBtn');
deleteBtn.addEventListener('click', deleteFunc);
This might help you:
https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
Find the solution.
const todo = document.getElementById('todo');
const enter = document.getElementById('enter');
const output = document.getElementById('output');
enter.addEventListener('click', () => {
listItem(todo);
});
let createListItem;
var deleteBtn;
let checkBtn;
var index = 0;
function listItem(todo) {
createListItem = document.createElement('li');
createListItem.innerText = todo.value;
createListItem.id= 'li' + index;
todo.value = '';
output.appendChild(createListItem);
checkBtn = document.createElement('button');
deleteBtn = document.createElement('button');
checkBtn.innerText = 'check';
deleteBtn.innerText = 'delete';
deleteBtn.id = 'btn' + index;
createListItem.append(checkBtn);
createListItem.append(deleteBtn);
checkBtn.classList.add('checkBtn');
deleteBtn.classList.add('deleteBtn');
deleteBtn.onclick = function() { deleteFunc(this); };
index += 1;
}
//output.addEventListener('click', deleteFunc);
function deleteFunc(e) {
var rowId = e.id.replace('btn','');
var row = document.getElementById('li'+rowId);
console.log('function called' + rowId);
//alert(rowId);
if (row != null) {
row.remove();
}
//console.log('function called');
//createListItem.remove();
}
<section class="controls">
<div>
<label for="todo">Enter a to-do</label>
<input type="text" name="todo" id="todo">
</div>
<span>
<button id="enter" class = "enter"><i class="fas fa-paper-plane"></i></button>
</span>
</section>
<section>
<ul id="output" class="output">
</ul>
</section>

How to delete an item on click from dynamically generated content javascript

I am working on a very basic task application with vanilla javascript. I have an array and from that, I am rendering the todos dynamically. I want to delete a todo when the checkbox is clicked.
I tried to create a form with the id of the todo in the hidden input and on change event submit the form and delete the todo but I'm generating all of them dynamically. Don't you think it will be a lot of code?
Is there a better way to do this?
Thank you
const todos = ['Complete the todo app', 'Write a script', 'Record a video', 'Publish the video']
const renderTodos = function (todos) {
document.querySelector('#todos').innerHTML = ''
todos.forEach(function (todo, index) {
const divElem = document.createElement('div')
divElem.classList.add(`item`, `item-${index}`)
document.querySelector('#todos').appendChild(divElem)
const checkboxElem = document.createElement('input')
checkboxElem.type = 'checkbox'
checkboxElem.name = 'deleteTodo'
document.querySelector(`.item-${index}`).appendChild(checkboxElem)
const titleElem = document.createElement('p')
titleElem.textContent = todo
document.querySelector(`.item-${index}`).appendChild(titleElem)
})
}
renderTodos(todos)
document.querySelector('#add-todo').addEventListener('submit', function (e) {
e.preventDefault()
if (e.target.elements.newItem.value === '') {
return
}
todos.push(e.target.elements.newItem.value)
e.target.elements.newItem.value = ''
renderTodos(todos)
})
<div id="todos"></div>
<form class="item" id="add-todo" action="">
<input
type="text"
name="newItem"
placeholder="New Item"
autocomplete="off"
/>
<button type="submit" name="list">+</button>
</form>
use remove method :
const DomParser = new DOMParser()
, todoAll = document.getElementById('todos')
, todoForm = document.getElementById('add-todo')
, todos = ['Complete the todo app', 'Write a script', 'Record a video', 'Publish the video']
;
function todoAdding(todo, index)
{
let elmZ = `<div class="item item-${index}"><input type="checkbox" name="deleteTodo"><p>${todo}</p></div>`;
let inZ = DomParser.parseFromString( elmZ, 'text/html');
todoAll.appendChild( inZ.body.firstChild )
}
function renderTodos(todos)
{
document.querySelector('#todos').innerHTML = ''
todos.forEach( todoAdding )
}
todoForm.onclick = e =>
{
e.preventDefault()
if (todoForm.newItem.value === '') return
todos.push(todoForm.newItem.value)
todoForm.newItem.value = ''
renderTodos(todos)
}
todoAll.onclick = e =>
{
if (!e.target.matches('input[type=checkbox]')) return
let suppIndex = todos.findIndex(todo=>todo===e.target.parentNode.querySelector('p').textContent)
todos.splice(suppIndex,1)
e.target.parentNode.remove()
}
renderTodos(todos) // initial case
<div id="todos"></div>
<form class="item" id="add-todo" action="">
<input
type="text"
name="newItem"
placeholder="New Item"
autocomplete="off"
/>
<button type="submit" name="list">+</button>
</form>

Dynamically get the newly added textbox value

I am dynamically adding a new textbox on a click event.
I am not able to get the value of each textbox, any help would be appreciated, thanks.
In my html page:
<div id="job-container">
<p>
<label>Job Property</label><br>
<input name="JobProperty[1][job_property]" />
</p>
</div>
<a href="javascript:" id="add-new-jobproperty"
onclick="createJobProperty()">Add new job</a>
In my Javascript code
let i = 2;
function createJobProperty() {
let template = `
<p>
<label>Job Property</label><br>
<input name="JobProperty[${i}][job_property]">
</p>`;
let container = document.getElementById('job-container');
let div = document.createElement('div');
div.innerHTML = template;
container.appendChild(div);
definedLength=i;
i++;
}
Try to add a class to inputs and then get them by className (since by tag name, you may have other inputs), and iterate through each of them to get values.
<input class='my-input' name="JobProperty[${i}][job_property]"> use this code to add a new input
var inputs = document.getElementsByClassName('my-input');
Array.from(inputs).forEach(function(element) {
console.log(element.value);
});
//or
Array.prototype.forEach.call(inputs, function(element) {
console.log(element.value);
});
You could try:
let i = 2;
function createJobProperty() {
let template = `
<p>
<label>Job Property</label><br>
<input name="JobProperty[` + i + `][job_property]">
</p>`;
let container = document.getElementById('job-container');
let div = document.createElement('div');
div.innerHTML = template;
container.appendChild(div);
let input = container.querySelector("input[name='JobProperty["+i+"][job_property]']");
// do what ever you want with the input
definedLength=i;
i++;
}

Categories