I am creating a to-do-list in javascript and everything was good until I had to deal with the local storage. I want to be able to refresh the page without losing what I put in my list just before.
I searched in many forums but I didn't find a similar case.
Here is a part of my HTML code : Just an ul and an input
Here is a part of my JS code : My function which creates li inside my ul
And here is a preview of my to do list : Hope it helps
So if I didn't explain my problem well enough, let me know and I will bring more precisions.
Thank for reading !
PS : I should specify that I already read the documentation on MDN and others websites and I understood the principle of localStorage but I'm struggling with the integration of this in my code. I saw lot of examples of its use but they are often too simple or on the contrary too different/hard to understand.
This is why I ask your help to have a little bit more personal response.
window.addEventListener('load', function()
{
var yourToDo = document.getElementById('myInput');
yourToDo.addEventListener('keydown', myFunction);
function myFunction(e)
{
if (e.keyCode == 13)
{
var line = document.createElement('div'); //This is my div which contains the 3 items which constitute a line
line.classList.add('myLine');
document.getElementById('myUl').appendChild(line);
var circle = document.createElement('i'); //The first item is a circle which can be check or unchecked
circle.id = 'myCircle';
document.querySelector('.myLine:last-child').appendChild(circle);
circle.addEventListener('click', function()
{
this.classList.toggle('fas');
this.classList.toggle('fa-check-circle');
this.classList.toggle('unstyled');
task.classList.toggle('crossedOut')
});
var task = document.createElement('li'); //The second item is a <li> which contains the value of the input
task.innerHTML = yourToDo.value.charAt(0).toUpperCase() + yourToDo.value.slice(1);
document.querySelector('.myLine:last-child').appendChild(task);
var trash = document.createElement('i'); //The third item is a trash which suppresses the whole line
trash.classList.add('fas');
trash.classList.add('fa-trash-alt');
document.querySelector('.myLine:last-child').appendChild(trash);
trash.addEventListener('click', function()
{
this.parentNode.remove();
});
yourToDo.value = '';
}
}
<section>
<ul id="myUl"></ul>
<label>
<input id="myInput" type="text" placeholder="Add your to do task" onfocus="this.placeholder = ''" onblur="this.placeholder = 'Add your to do task'">
</label>
</section>
I don't think that localStorage works in the code-snippets but try this in your own code.
I added a function to your code that gets all localStorage items and creates a list item for each on page load. Also added where you should save it to localStorage in your original function.
Comments are added throughout.
Hope this helps
window.addEventListener('load', function() {
var yourToDo = document.getElementById('myInput');
yourToDo.addEventListener('keydown', myFunction);
function myFunction(e) {
if (e.keyCode == 13) {
var line = document.createElement('div'); //This is my div which contains the 3 items which constitute a line
line.classList.add('myLine');
document.getElementById('myUl').appendChild(line);
var circle = document.createElement('i'); //The first item is a circle which can be check or unchecked
circle.id = 'myCircle';
document.querySelector('.myLine:last-child').appendChild(circle);
circle.addEventListener('click', function() {
this.classList.toggle('fas');
this.classList.toggle('fa-check-circle');
this.classList.toggle('unstyled');
task.classList.toggle('crossedOut')
});
var task = document.createElement('li'); //The second item is a <li> which contains the value of the input
task.innerHTML = yourToDo.value.charAt(0).toUpperCase() + yourToDo.value.slice(1);
//Set loacl storage item
localStorage.setItem(yourToDo.value.charAt(0).toUpperCase() + yourToDo.value.slice(1), yourToDo.value.charAt(0).toUpperCase() + yourToDo.value.slice(1));
document.querySelector('.myLine:last-child').appendChild(task);
var trash = document.createElement('i'); //The third item is a trash which suppresses the whole line
trash.classList.add('fas');
trash.classList.add('fa-trash-alt');
document.querySelector('.myLine:last-child').appendChild(trash);
trash.addEventListener('click', function() {
this.parentNode.remove();
});
yourToDo.value = '';
}
}
});
function load() {
//Create each item (as you did above)
function create(item) {
var yourToDo = document.getElementById('myInput');
var line = document.createElement('div'); //This is my div which contains the 3 items which constitute a line
line.classList.add('myLine');
document.getElementById('myUl').appendChild(line);
var circle = document.createElement('i'); //The first item is a circle which can be check or unchecked
circle.id = 'myCircle';
document.querySelector('.myLine:last-child').appendChild(circle);
circle.addEventListener('click', function() {
this.classList.toggle('fas');
this.classList.toggle('fa-check-circle');
this.classList.toggle('unstyled');
task.classList.toggle('crossedOut')
});
var task = document.createElement('li'); //The second item is a <li> which contains the value of the input
//Set innerHTML to item that you ar passing in for loop below
task.innerHTML = item;
document.querySelector('.myLine:last-child').appendChild(task);
var trash = document.createElement('i'); //The third item is a trash which suppresses the whole line
trash.classList.add('fas');
trash.classList.add('fa-trash-alt');
document.querySelector('.myLine:last-child').appendChild(trash);
trash.addEventListener('click', function() {
this.parentNode.remove();
});
yourToDo.value = '';
}
//Create an array to store all local storage items
var values = [],
keys = Object.keys(localStorage),
a = keys.length;
//Push items to array
while (a--) {
values.push(localStorage.getItem(keys[a]));
}
//Create a for loop and loop through all array items and pass each item value to the create funtion above
for (let i = 0; i < Object.keys(localStorage).length; i++) {
create(values[i])
}
}
//Call load on page load up (it would actually be better if you add this in your window "load" listener above )
load();
//Hope this helps!
<section>
<ul id="myUl"></ul>
<label>
<input id="myInput" type="text" placeholder="Add your to do task" onfocus="this.placeholder = ''" onblur="this.placeholder = 'Add your to do task'">
</label>
</section>
Related
I posted this question before, among others. But it was suggested I need to ask a more specific or focused question.
I am working on an output history log on a single page. And I want to make it so each output it's self is contained in box object that can be closed or deleted individually. Like this.
Now I have managed to get everything working to the point where it will nicely output to a box with a close button. However the close button it's self will not function in this case.
So, I am trying to output it like this...
HTML:
<p>History log:</p><br><div style="white-space:pre-wrap"><ul
id="outputListItem" class="boxcontainer"></ul></div>
SCRIPT:
document.getElementById("Add").onclick = function(e) {
convertOutput();
}
function convertOutput(){
//this is the part I have been trying to get working
convertOutput.addEventListener('close', function() {
this.parentElement.style.display = 'none';
}
});
var output = document.getElementById("output").value;
var li = document.createElement('li');
li.className = "containedboxes";
var dateTime = todayDateTime();
li.innerHTML = "<time id='time'>" + dateTime +"</time><br /> <br />"+ output
+"<br /><br /><span class='close'>×</span>";
document.getElementById('outputListItem').prepend(li);
}
And the script to close the box:
var closebtns = document.getElementsByClassName("close");
var i;
for (i = 0; i < closebtns.length; i++) {
closebtns[i].addEventListener("click", function() {
this.parentElement.style.display = 'none';
});
}
It was suggested to me on the last question I posed I should use convertOutput() right after addEventListener() loop immediately after it. If this is how you do it, i am still quite new to JavaScript, so not sore how to properly do this. I created a fiddle for this also, but for some reason I can't get the script to run properly in the fiddle, But all the code is there to see.
I am looking to solve this using vanilla JavaScript.
I created an example for you. Hopefully this helps you get going :) A couple things to note, I use a data attribute to store the index for the item in the array, so you can delete it when you click on the list item.
document.addEventListener('DOMContentLoaded', function(){
let nameEl = document.querySelector("#name");
let submitEl = document.querySelector("#submit-name");
let historyEl = document.querySelector(".history-list");
let historyList = [
{ name: 'Mitch'},
{ name: 'Max'},
{ name: 'Mike'},
];
function addToList(arr) {
// Clear up list and then update it
while(historyEl.firstChild) {
historyEl.removeChild(historyEl.firstChild);
}
// Update the list with the historyList
for(let item in historyList) {
let name = historyList[item].name;
let listContent = document.createElement("li");
listContent.textContent = name;
// We will use the index to remove items from the list
listContent.setAttribute('data-value', item);
listContent.addEventListener("click", removeFromList)
historyEl.appendChild(listContent);
}
}
function removeFromList(index) {
// Takes the index of the object, and will later remove it
console.log("Removed Item " + this.dataset.value);
historyList.splice(index, 1);
addToList(historyList);
}
addToList(historyList);
submitEl.addEventListener("click", function(event) {
if(nameEl.value) {
// Add the name to the start of the history list array.
historyList.unshift({ name: nameEl.value})
nameEl.value = '';
// Update the dom with the new array
addToList(historyList);
}
});
});
<label for="name">Type Name</label>
<input type="text" name="name" id="name">
<button id="submit-name">Submit Name</button>
<ul class="history-list"></ul>
Hopefully this gives you a good idea on how to get the task done and let me know if you have any questions :)
Your boxes don't respond to the click event simply because your script crashes before the events even get attached to it.
The following block right at the beginning:
document.getElementById("Add").onclick = function(e) {
convertOutput();
}
tries to add a click listener to the HTML element Add which does not exist. If you either remove the code block or add the appropriate element your boxes will have it's click functionality.
I want to create a to-do list with an array, functions and js must be separated from HTML. The HTML part is fine, but I have troubles finishing the functions.
Also the EventListener works.
<form id="todoForm">
<input id="todoInput" type="text">
<button type="button" id="button">Add your To Do</button>
</form>
<ol id="toDoList"></ol>
We have the array 'todos', the button click is noticed, and the function 'addTodo' is called, which (I hope so) pushes the input into the array.
I don't know how to call next function, which should create the array items as li elements, that is the place where I need help. The content in function 'printTodos' is garbage.
var todos = [];
document.getElementById('button').addEventListener('click', function
addTodo () {
todos.push('input')
function printTodos () {
var item = document.createElement("li");
var node = createTextNode(input);
// I am stuck
}
});
You are very close, except you do not want to create the addTodo and printTodos function inside your click event listener.
What you can instead do is define the two functions outside of it, and call them inside your click event listener, like so:
var todos = [];
function addTodo() {
var inputValue = document.getElementById('todoInput').value;
todos.push(inputValue);
}
function printTodos() {
var list = document.getElementById('toDoList');
list.innerHTML = ''; //Reset the list content whenever we print the todos, so we don't end up with duplicate todo items.
for (var i = 0; i < todos.length; i++) {
var li = document.createElement('li');
var listItem = li.appendChild(document.createTextNode(todos[i]));
list.appendChild(listItem);
}
}
document.getElementById('click', function() {
addTodo();
printTodos();
});
What we do here is, in the addTodo function, we programmatically get the text typed into the todoInput and add it to the array. Then, in the printTodos function, we loop over all the entered todos and create <li> element filled with the todo text. At the end, we append the new list item to the toDosList.
I've made a fully working code pen example for you. Please take a look on it or go with the above answer. Here is what you need https://codepen.io/waleedbinkhalid/pen/aRvwmo
var todos = [];
document.getElementById('button').addEventListener('click', function () {
var list = $('#toDoList');
var todoInput = $('#todoInput').val();
todos.push(todoInput)
var item = document.createElement("li");
for (var i = 0; i < todos.length; i++) {
var listItem = $(document.createTextNode(todos[i]));
list.append(document.createTextNode(todos[i]));
}
});
`
I am building a todo app where I am dynamically generating tasks using javascript.[]
I generate following equivalent html from js whenever I click on the add button:
<div class="row datasection">
<div class="todo">
<div class="databox col s6 offset-s1 waves-effect">
<p class="checkglyph1 checkglyph2">Task no 1</p>
<a>
<i class="material-icons checkglyph checkglyph1 checkglyph2 glyphcolor">check</i>
</a>
</div>
</div>
</div>
Now what I want is whenever I click event on the task created it should become yellow in colour.I have written the code to this. below.However it works fine only when there is one task created.If there is multiple then the last task works well but actionlistener on the first one does not seem to be working.I am not able to figure out where the code is breaking.
var glyph= document.querySelectorAll(".glyphcolor");
for (var i = 0; i < glyph.length; i++) {
glyph[i].addEventListener('click', function(event) {
this.classList.toggle("checkglyph1");
});
}
The actual snippet
//declaration
var calendardata = document.getElementById('date1');
var addbutton = document.querySelector('.addbutton');
var todo = document.querySelector('.todo');
addbutton.addEventListener('click', function() {
/* body to return the html */
if (data.value) {
var newdiv = document.createElement("div"); // Create a <button> element
newdiv.classList.add("databox", "col", "s6", "waves-effect");
//console.log(newdiv);
todo.appendChild(newdiv);
//console.log(newdiv.parentNode);
var newpar = document.createElement("p");
newpar.classList.add("checkglyph1", "checkglyph2");
var node = document.createTextNode(data.value + "." + " " +
calendardata.value);
var newa = document.createElement("a");
newdiv.appendChild(newa)
var newglyph = document.createElement("i");
newglyph.classList.add("material-icons", "checkglyph", "checkglyph1",
"checkglyph2", "glyphcolor");
var node1 = document.createTextNode("check");
newa.appendChild(newglyph);
newglyph.append(node1);
newpar.appendChild(node);
newdiv.appendChild(newpar);
data.value = "";
calendardata.value = "";
created = true;
// console.log("before glyh created");
//code to perform action on the click of the tick symbol
var glyph = document.querySelectorAll(".glyphcolor");
var par = document.getElementsByClassName('checkglyph2');
for (var i = 0; i < glyph.length; i++) {
//console.log("Inside the loop");
glyph[i].addEventListener('click', function.bind(event) {
this.classList.toggle("checkglyph1");
//console.log('Inside the click');
//console.log(i);
});
}
}
})
What's happening that when they other tasks are created they aren't being collected in the Node Collection, thus they have no event listener. What you can do is instead add the event listener to the container and change whichever item that was clicked in:
document.querySelector('ul').addEventListener('click', changeClass);
document.querySelector('#button').addEventListener('click', addLi);
function changeClass(e){
e.target.closest('li').classList.toggle('checkglyph1');
}
function addLi(e){
const new_li = document.createElement('li');
new_li.textContent = document.querySelectorAll('li').length + 1;
new_li.classList.add('checkglyph1');
document.querySelector('ul').appendChild(new_li);
}
li:not(.checkglyph1) {
background: #f00;
}
<button id="button">Add li</button>
<ul>
<li class="checkglyph1">1</li>
<li class="checkglyph1">2</li>
<li class="checkglyph1 red">3</li>
<li class="checkglyph1">4</li>
<li class="checkglyph1">5</li>
<li class="checkglyph1">6</li>
<li class="checkglyph1">7</li>
<li class="checkglyph1">8</li>
<li class="checkglyph1">9</li>
<li class="checkglyph1">10</li>
</ul>
The problem with your code is that everytime you click on ".addbutton", you're going through all of the ".glyphcolor"s element's in your DOM and adding a new onclick event listener. The last one created will work, but the others will have multiple event listeners repeated (yes, this is possible). So, when you click on an element that has two events telling it to toggle the "checkglyph1" class, it will do it twice. And of course, it will not change at all. You can easily see this happening, because all the odd elements in your page must work (they will toggle the class by odd times), while the even ones must not.
This can be corrected by adding the event listener directly on the element when you create it on the page. The code below must work fine:
//declaration
var calendardata = document.getElementById('date1');
var addbutton = document.querySelector('.addbutton');
var todo = document.querySelector('.todo');
addbutton.addEventListener('click', function() {
if (data.value) {
var newdiv = document.createElement("div"); // Create a <button> element
newdiv.classList.add("databox", "col", "s6", "waves-effect");
//console.log(newdiv);
todo.appendChild(newdiv);
//console.log(newdiv.parentNode);
var newpar = document.createElement("p");
newpar.classList.add("checkglyph1", "checkglyph2");
var node = document.createTextNode(data.value + "." + " " +
calendardata.value);
var newa = document.createElement("a");
newdiv.appendChild(newa)
var newglyph = document.createElement("i");
newglyph.classList.add("material-icons", "checkglyph", "checkglyph1",
"checkglyph2", "glyphcolor");
var node1 = document.createTextNode("check");
newa.appendChild(newglyph);
newglyph.append(node1);
newpar.appendChild(node);
newdiv.appendChild(newpar);
data.value = "";
calendardata.value = "";
created = true;
newglyph.addEventListener('click', function(event) {
event.preventDefault(); // Just to prevent non desired effects of click
newglyph.classList.toggle('checkglyph1');
}
}
}
And just small clarifications: you don't need the "bind" in your actual code.
glyph[i].addEventListener('click', function.bind(event) { // just type function(event)
this.classList.toggle('checkglyph1');...
I deleted the button part in my script but not even the first part of my function is working where I type in input box and suppose to be added to the ...I don't understand why. When I run the code without the buttons code which is titled " //BUTTON creation " I get no error but no item is being added to the list. So I have two problems Items aren't being added to my list and aren't displaying and also if I include the button part its saying an error "list.appendChild is not a function"
<input type="text" placeholder="Enter an Activity" id="textItem">
<img src="images/add-button.png" id="addButton">
<div id="container">
<ul class="ToDo">
<!--
<li>
This is an item
<div id="buttons">
<button ></button>
<img src="images/remove-icon.png"id="remove">
<button id="complete"></button>
<img src="images/complete-icon.jpg" id="complete">
</div>
</li>
!-->
</ul>
</div>
<script type="text/javascript">
//Remove and complete icons
var remove = document.createElement('img').src =
"images/remove-icon.png";
var complete = document.createElement('img').src = "images/complete-icon.jpg";
//user clicks add button
//if there is text in the item field we grab the item into var text
document.getElementById("addButton").onclick = function()
{
//value item is the text entered by user
var value = document.getElementById("textItem").value;
//checks if there is a value typed
if(value)
{
addItem(value);
}
//adds a new item to the ToDo list
function addItem(text)
{
var list = document.getElementsByClassName("ToDo");
//created a varibale called item that will create a list item everytime this function is called
var item = document.createElement("li");
//this will add to the innerText of the <li> text
item.innerText = text;
//BUTTON creation
var buttons = document.createElement('div');
buttons.classList.add('buttons');
var remove = document.createElement('buttons');
buttons.classList.add('remove');
remove.innerHTML = remove;
var complete = document.createElement('buttons');
buttons.classList.add('complete');
complete.innerHTML = complete;
buttons.appendChild(remove);
buttons.appendChild(complete);
list.appendChild(buttons);
list.appendChild(item);
}
}
</script>
The problem is in the line:
var list = document.getElementsByClassName("ToDo");
list.appendChild(item);
The line var list = document.getElementsByClassName("ToDo"); will provide a collection, notice the plural name in the api.
You need to access it using :
list[0].appendChild(item);
There are other problems too in the code but hopefully this gets you going!
There are a couple of issues in your code that need to be addressed to get it to work properly.
1) You are creating your image elements and then setting the variables to the src name of that image and not the image object itself. When you use that reference later on, you are only getting the image url and not the element itself. Change var remove = document.createElement('img').src = "images/remove-icon.png" to this:
var removeImg = document.createElement('img')
removeImg.src = "images/remove-icon.png";
2) As #Pankaj Shukla noted, inside the onclick function, getElementsByClassName returns an array, you will need to address the first item of this array to add your elements. Change var list = document.getElementsByClassName("ToDo") to this:
var list = document.getElementsByClassName("ToDo")[0];
3) For your buttons, you are trying to creating them using: var remove = document.createElement('buttons'). This is invalid, buttons is an not the correct element name, its button. Additionally, you are re-declaring the variables remove and complete as button objects, so within the onclick function it reference these buttons, not the images you defined earlier. So when you assign the innerHTML to remove and complete, you are assigning the buttons innerHTML to itself. The solution is to change the image variables to something different.
4) Finally, also relating to the buttons, you are assigning the innnerHTML to an image object, that's incorrect. You can either insert the html text of the img directly, or append the image object as a child of the button, similar to how the button is a child of the div.
The updated code with all these changes looks like this:
//Remove and complete icons
var removeImg = document.createElement('img');
removeImg.src = "images/remove-icon.png";
var completeImg = document.createElement('img');
completeImg.src = "images/complete-icon.jpg";
//user clicks add button
//if there is text in the item field we grab the item into var text
document.getElementById("addButton").onclick = function() {
//value item is the text entered by user
var value = document.getElementById("textItem").value;
//checks if there is a value typed
if (value) {
addItem(value);
}
//adds a new item to the ToDo list
function addItem(text) {
var list = document.getElementsByClassName("ToDo")[0];
//created a varibale called item that will create a list item everytime this function is called
var item = document.createElement("li");
//this will add to the innerText of the <li> text
item.innerText = text;
//BUTTON creation
var buttons = document.createElement('div');
buttons.classList.add('buttons');
var remove = document.createElement('button');
remove.classList.add('remove');
remove.appendChild(removeImg);
var complete = document.createElement('button');
complete.classList.add('complete');
complete.appendChild(completeImg);
buttons.appendChild(remove);
buttons.appendChild(complete);
list.appendChild(buttons);
list.appendChild(item);
}
}
essentially what I am trying to do is to create a button on a created text node in js. Then find the value of spmet and remove the question (spmet) from the database.
However I can't figure out how to properly reference it, and find the specific value that I want deleted. (so other picture, remove that question from database when I press the "x")
this is the the way to remove questions
This is the firebase layout
var btn = document.createElement("BUTTON");
var btnText = document.createTextNode("x"); //create button
btn.appendChild(btnText);
tekst.appendChild(btn);
btn.id = "questionBtn";
//bytter enter som gir linjeskift til <br>
tekst.innerHTML = tekst.innerHTML.replace(/\n/g, '<br>');
chat.appendChild(bubble);
setTimeout(function(){
chat.classList.add('visible')
}, 1);
chat.scrollTop = chat.scrollHeight;
console.log(bubble);
// Function to remove the question on the button generated
tekst.onclick = function removeQ(){
window.alert("Knapp funker");
var ref = database.ref();
ref.child('spm')
.orderByChild('spmet')
.equalTo(spmet)
.once('value', function(snap) {
//remove the specific spmet parent
window.alert(snap.val());
});
document.getElementById("cont1").removeChild(bubble); // removes text from page
var spmRef = ??
spmRef.remove(); //can't reference properly
}
When you generate the HTML element for each question, keep the ID of that question as an attribute on that HTML element:
ref.child("spm").on("child_added", function(snapshot) {
var div = document.createElement("div");
div.id = snapshot.key;
div.innerText = snapshot.child("spmet").val();
div.onclick = onSpmClick;
questionContainer.appendChild(div);
});
Now when the user clicks on one of the questions, you can get the key from that div and remove it:
function onSpmClick(e) {
var key = e.target.id;
ref.child("spm").child(key).remove();
}