I'm creating a basic to do list in Vanilla JS, I'm using Handlebars to keep the HTML & JS separate.
Everything was going fine till I came to the delete method. Because my delete button is inside my HTML and not created inside my JS I'm finding it hard to select and delete items from the array.
I thought I'd found a way around it by looping over them but the issue with this is it tries to grab the buttons on page load, and so it returns always an empty array as on page load there are no delete buttons as no to do has been added at that point.
I've also tried putting the delete method inside the add method to counter this but this also presented issues.
Simply, can someone give me an example of a working delete method that removes the relevant item from the array using splice.
Cheers
HTML
<input id="add-to-do-value" type="text" placeholder="Add to do">
<button id="add-to-do">Add</button>
<div id="to-do-app"></div>
<script type="text/javascript" src="js/handlebars.js"></script>
<script id="to-do-template" type="text/template">
<ul>
{{#this}}
<div>
<li id={{id}}>
{{value}}
<button class="delete-btn" id={{id}}>Delete</button>
</li>
</div>
{{/this}}
</ul>
</script>
<script type="text/javascript" src="js/app.js"></script>
JS
(function() {
// Data array to store to dos
var data = [];
// Cache dom
var toDoApp = document.getElementById('to-do-app');
var toDoTemplate = document.getElementById('to-do-template');
var addToDo = document.getElementById('add-to-do');
var addToDoValue = document.getElementById('add-to-do-value');
var toDoTemplate = Handlebars.compile(toDoTemplate.innerHTML);
// Render HTML
var render = function() {
toDoApp.innerHTML = toDoTemplate(data);
}
// Add to dos
var add = function() {
var toDoValue = addToDoValue.value;
if(toDoValue) {
var toDoObj = {
value: toDoValue,
id: Date.now(),
}
data.push(toDoObj);
}
render();
}
// Delete to dos
var deleteBtn = document.querySelectorAll('.delete-btn');
for(i=0; i<deleteBtn.length; i++) {
deleteBtn[i].addEventListener("click", function(){
for(j=0; j<data.length; j++) {
if(data[j].id == this.id) {
data.splice(data[j], 1);
render();
}
}
});
}
// Bind events
addToDo.addEventListener("click", add);
})();
The fact that you're using Handlebars makes the whole thing unnecessary complex. I would suggest that you don't use innerHTML, but other parts of the DOM API instead to be able to easily access the elements you need. For more complex todo items, I would consider using <template>s.
Anyway, you have to bind the event listener for removing the item when you create the new item (i.e. in the add function):
var todos = [];
var input = document.querySelector('input');
var addButton = document.querySelector('button');
var container = document.querySelector('ul');
var add = function () {
var content = input.value;
input.value = '';
var id = Date.now();
var li = document.createElement('li');
li.appendChild(document.createTextNode(content));
var button = document.createElement('button');
button.textContent = 'Delete';
button.addEventListener('click', remove.bind(null, id));
li.appendChild(button);
todos.push({ content, id, element: li });
container.appendChild(li);
};
var remove = function (id) {
var todo = todos.find(todo => todo.id === id);
container.removeChild(todo.element);
todos = todos.filter(t => t !== todo);
};
addButton.addEventListener('click', add);
<input type="text" placeholder="Add to do">
<button>Add</button>
<ul></ul>
Related
Program starts with displaying all notes from localstore, but when I click the addButton it display my current note only.
I want to show all the notes and after click event new note will add with previous notes.
let addButton = document.querySelector(".addBtn");
let userNotes = [];
displayNotes();
//addButton Event Listner
addButton.addEventListener("click", function(e) {
e.preventDefault();
//get the user inputs
let userInputs = {
title: document.getElementById("title_input").value,
description: document.getElementById("des_input").value
};
//push user inputs to arrays
userNotes.push(userInputs);
document.querySelector("form").reset();
//store to the localstorage
localStorage.setItem("Notes", JSON.stringify(userNotes));
//display to the user
displayNotes();
});
function displayNotes() {
let gettingNotes = localStorage.getItem("Notes");
let allNotes = JSON.parse(gettingNotes);
let html = "";
allNotes.forEach(element => {
html += `
<div class="single-item">
<h2 class="single-item-title">
${element.title}
</h2>
<p class="single-item-description">
${element.description}
</p>
</div>
`;
});
document.querySelector(".item-list").innerHTML = html;
}
It happens because you set userNotes to be an empty array and you add only your current note. Try to initialize userNotes with a value from localStorage, i.e
let userNotes = JSON.parse(localStorage.getItem('Notes')) || []
Also, then you are able to use userNotes variable in displayNotes function, instead of allNotes.
I want to make my own grocery/task list in JavaScript and HTML. I can add things to the list no problem, but I have two problems:
If I added items to the list, then press a "clear" button to erase everything in the list, it works. However, when I add things back into the list after clearing it, my console notifies me that my array is still empty, despite new tasks being shown on the screen.
My second problem is bigger. See, all my tasks are in what I thought was an array, but are actually an HTML collection. So, when I try to set an onclick event, it just won't run. I have no idea why and I tried using the following question: Removing HTMLCollection elements from the DOM
. It didn't work. I tried using item, HTMLCollection item() Method. Didn't work. My final attempt was using for(let thing of... but still no results.
Here is my code:
let listGro = document.getElementById("list");
let aButton = document.getElementById("add");
let cButton = document.getElementById("clear");
let tasks = document.getElementsByTagName("li");
aButton.onclick = function addItem() {
let newThing = prompt("What do you want to add?"); // asking what the person wants
if (newThing) { // checking if the user actually added something
let newItemList = document.createElement("li"); //create item list
newItemList.className = "item";
let newItemListText = document.createTextNode(newThing); // create text
newItemList.appendChild(newItemListText); // append text
listGro.appendChild(newItemList); // append new element
console.log(`New task added\nNumber of tasks in the list: ${tasks.length}`);
} else {
alert("You can't add nothing");
}
};
cButton.onclick = function clearList() {
var conf = confirm("Are you sure you want to clear the list?");
if (conf && tasks.length != 0) {
for (let i = 0; i < tasks.length; i++) {
tasks[i].style.display = "none";
}
tasks = [];
}
}
for(let thing of tasks) {
//tasks[i].onclick = function removeOrEditItem() {
/*let demand = prompt("Do you want to edit or remove the item?\nPlease answer with 'edit' or 'remove'\nIf this is a mistake, just enter nothing.");
if (demand === "edit") {
let editItem = prompt("What is your new thing?");
tasks[i].innerHTML = editItem;
} else if (demand === "remove") {
tasks[i].splice(i, 1);
}
console.log("clicked");
};*/
// The thing above was a previous attempt with for(let i; i< items.length... you can work with that or below.
thing.onclick = function removeTask() {
thing.style.display = "none";
console.log("removed");
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Website template</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"/>
</head>
<body>
<h1>Grocery lists manager</h1>
<h2>Welcome! here you can manage your grocery list!</h2>
<ul id = "list"><span>List:</span></ul>
<button id = "add">Add</button>
<button id = "clear">Clear list</button>
</body>
<script src = "script.js"></script>
</html>
The list of tasks remains empty when you clear it, because you're overwriting an HTML collection with an empty array. You assigned document.getElementsByTagName("li"); to tasks, which is a live collection. Reassigning [] to it will give it a non-live empty array. Instead of visually hiding the tasks with tasks[i].style.display="none and manually resetting the tasks collection, you should remove each task element from the DOM and let your live collection update itself automatically.
Removing a collection from the DOM has some pitfalls, but would typically be accomplished with something like the following:
while(tasks.length > 0) {
tasks[0].parentNode.removeChild(tasks[0]);
}
Your event listeners are not being added because the code that adds them only runs once, when the script is loaded. And of course at the instance when the script is loaded, there are no tasks in the list. Add the event listener to the individual newItemList when it's first added.
Make sure that you remove the task from the DOM and don't just hide it.
newItemList.onclick = function removeTask() {
newItemList.parentNode.removeChild(newItemList);
console.log("removed");
}
Your complete JavaScript might look like:
let listGro = document.getElementById("list");
let aButton = document.getElementById("add");
let cButton = document.getElementById("clear");
let tasks = document.getElementsByTagName("li");
aButton.onclick = function addItem() {
let newThing = prompt("What do you want to add?"); // asking what the person wants
if (newThing) { // checking if the user actually added something
let newItemList = document.createElement("li"); //create item list
newItemList.className = "item";
let newItemListText = document.createTextNode(newThing); // create text
newItemList.appendChild(newItemListText); // append text
newItemList.onclick = function removeTask() {
newItemList.parentNode.removeChild(newItemList);
console.log("removed");
}
listGro.appendChild(newItemList); // append new element
console.log(`New task added\nNumber of tasks in the list: ${tasks.length}`);
} else {
alert("You can't add nothing");
}
};
cButton.onclick = function clearList() {
var conf = confirm("Are you sure you want to clear the list?");
if (conf && tasks.length != 0) {
while(tasks.length > 0) {
tasks[0].parentNode.removeChild(tasks[0]);
}
}
};
I have successfully created a button which adds text to the webpage however I do not know a viable way to remove text once this has been created. The js code I have is:
var addButtons = document.querySelectorAll('.add button');
function addText () {
var self = this;
var weekParent = self.parentNode.parentNode;
var textarea = self.parentNode.querySelector('textarea');
var value = textarea.value;
var item = document.createElement("p");
var text = document.createTextNode(value);
item.appendChild(text)
weekParent.appendChild(item);
}
function removeText() {
//document.getElementbyId(-).removeChild(-);
}
for (i = 0; i < addButtons.length; i++) {
var self = addButtons[i];
self.addEventListener("click", addText);
}
I have viewed various sources of help online including from this site however I simply cannot get any to work correctly. Thank you in advance.
Sure, it should be easy to locate the added <p> tag relative to the remove button that gets clicked.
function removeText() {
var weekParent = this.parentNode.parentNode;
var item = weekParent.querySelector("p");
weekParent.removeChild(item);
}
If there is more than 1 <p> tag inside the weekParent you will need a more specific querySelector.
Here's the code...
https://jsfiddle.net/6n2k65zs/
Try add a new item, you'll see its not working for some reason but it should be...
I can't spot any errors in the code, can someone help me out please?
And does anyone know any good debuggers? debugging JS is a nightmare!
Thanks.
<!DOCTYPE html>
<html>
<head>
<title>JavaScript To-Do List</title>
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<input id="input" type="text">
<button id="btn">Add</button>
<hr>
<ul id="todo">
</ul>
<ul id="done">
</ul>
<!-- javascript anonymous self-invoking function -->
<!-- Function expressions will execute automatically -->
<script>
// from outside the action you wont be able to access the variables
// prevents another variable with a same name from conflicting
(function(){
var input = document.getElementById('input');
var btn = document.getElementById('btn');
// Object for the lists
// the reason im using ID is because ID can only be named once rather than a class which can be named 100's of times
var lists = {
todo:document.getElementById('todo'),
done:document.getElementById('done')
};
/* Parameter is string
create a list element which is stored in 'el' and returns it
*/
var makeTaskHtml = function(str, onCheck) {
var el = document.createElement('li');
var checkbox = document.createElement('input');
var label = document.createElement('span');
label.textContent = str;
checkbox.type = 'checkbox';
checkbox.addEventListener('click', onCheck);
// el.textContent = str;
// can use this method to move an element from one element to another
el.appendChild(checkbox);
el.appendChild(label);
// Text content is grabbing the text from the text box and storing it in variable el.
return el;
};
var addTask = function(task) {
lists.todo.appendChild(task);
};
var onCheck = function(event){
var task = event.target.parentElement; //targets the item clicked
var list = task.parentElement.id;
//lists.done.appendChild(task);
//swaps the 2 objects around
lists[list === 'done' ? 'todo' : 'done'].appendChild(task);
this.checked = false;
input.focus();
};
var onInput = function() {
var str = input.value.trim; // trim removes white space...
if (str.length > 0) {
addTask(makeTaskHtml(str, onCheck));
input.value = '';
input.focus();
}
};
btn.addEventListener('click', onInput);
input.addEventListener('keyup', function(event){
var code = event.keyCode;
console.log(code);
if (code === 13) {
onInput();
}
});
input.focus();
addTask(lists.todo, makeTaskHtml('Test done', onCheck));
}());
</script>
</body>
</html>
It appears to me you are not calling trim as a method, but accessing it as a variable?
Try add the () in trim:
var onInput = function() {
var str = input.value.trim(); // trim removes white space...
Your addTask function is being called with 3 parameters:
addTask(lists.todo, makeTaskHtml('Test done', onCheck));
but the function definition for addTask only takes one parameter:
var addTask = function(task)
so you need to just call addTask with just makeTaskHtml parameter, and not lists.todo which is already referenced inside the addTask function or onCheck
Or for debugging in Chrome, try Cmd-Alt–I in (Mac) or Ctrl-Alt-I (Windows).
First of all, you shouldn't put your scripts inline in JSFiddle – put them in the JS box to protect everyone's sanity! It's what it's made for...
There are other issues in the code, but the main issue seems to be in this line:
var str = input.value.trim;
Here, you're assigning str to the JS function trim. You want to assign it the the results of trim(), so try:
var str = input.value.trim();
You're still getting other errors in the console, but the basics seem to work.
I have the following script
var counter = 0;
function appendText(){
var text = document.getElementById('usertext').value;
if ( document.getElementById('usertext').value ){
var div = document.createElement('div');
div.className = 'divex';
var li = document.createElement('li');
li.setAttribute('id', 'list');
div.appendChild(li);
var texty = document.createTextNode(text);
var bigdiv = document.getElementById('addedText');
var editbutton = document.createElement('BUTTON');
editbutton.setAttribute('id', 'button_click');
var buttontext = document.createTextNode('Edit');
editbutton.appendChild(buttontext);
bigdiv.appendChild(li).appendChild(texty);
bigdiv.appendChild(li).appendChild(editbutton);
document.getElementById('button_click').setAttribute('onClick', makeAreaEditable());
document.getElementById('usertext').value = "";
counter++;
}
};
var makeAreaEditable = function(){
alert('Hello world!');
};
I want the makeAreaeditable function to work when the Edit button is pressed(for each of the edit buttons that are appended under the textarea).. In this state, the script, alerts me when i hit the Addtext button.
the following is the html. P.S. i need this in pure javascript, if you can help. thanks
<textarea id="usertext"></textarea>
<button onClick="appendText()">Add text </button>
<div id="addedText" style="float:left">
</div>
instead of:
document.getElementById('button_click').setAttribute('onClick', makeAreaEditable());
you need to do this:
editbutton.onclick = makeAreaEditable;
the function's name goes without brackets unless you want to execute it
instead of obtaining the element from the DOM using document.getElementById('button_click')
you can use the editbutton variable already created. this object is the DOM element you are looking for
SIDE NOTE:
the standard way to do it is to add the onclick property before appending the element