grhu grhu - 6 months ago 40
Javascript Question

localStorage not loading all keys

I have two problems with my simple to-do app. I've decided to add an option to save your tasks in local Storage and load them next time you visit the app. I've managed to write a function that saves all items by clicking "Zapisz" button in localStorage, but when I reload the page to load them, it cuts 1 or 2 of the items.

Also, I can't figure out how to add appropriate class to saved item (there are two different classes, "item" and "itemDone"). So this is my problem, and here is the JS code:

(function() {
/* Load items from localStorage if there are any */
window.addEventListener("DOMContentLoaded", function() {
var myList = document.getElementById("list");
for (var i = 0; i < localStorage.length; i++) {
var item = document.createElement("li");
localKey = localStorage.getItem(localStorage.key(i));
item.innerHTML = JSON.parse(localKey);
if (item.innerHTML.indexOf("check") != -1) {
item.className = "itemDone";
} else {
item.className = "item";
}
myList.appendChild(item);
var closers = document.getElementsByClassName("closer");
for (var i = 0; i < closers.length; i++) {
closers[i].addEventListener("click", deleteItem);
}
};
});

/* This function deletes parent element of clicked element - in this case a clicked element will be a "span" element with "closer" class, and it's parent is "li" element */
function deleteItem() {
this.parentNode.parentNode.removeChild(this.parentNode);
};

/* this function allows to delete item from the loist */
var x = document.getElementsByClassName("closer"); // get all elements with "closer" class
for (var i = 0; i < x.length; i++) { // simple loop that adds eventListener to all elements with "closer" class
x[i].addEventListener("click", deleteItem); // on click function "deleteItem" is invoked
};

/* This function adds "li" element to the list with text value from input #newInput, and then adds a span with innertext "X" and class .closer */
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
newListItem.className = ""
if (itemText === "") { // if input value 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
x.addEventListener("click", deleteItem, false);
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 value to null
}
};
/* addButton reffers to the button used to add new elements to the list. After clicking it, function "addItem" is called */
var addButton = document.getElementById("createNew"); // fetch the "createNew" button
addButton.addEventListener("click", addItem); // add click event to "createNew" button and run function
/* This function clears all added elements from the list */

/* This script adds a method in which you can also add a new "li" item with "enter" keydown */
var textAdd = document.getElementById("newInput"); // target input
textAdd.addEventListener ("keypress", function (e) { // add event listener on "keypress"
var key = e.which || e.keyCode; // which key is being used
if (key === 13){ // if pressed key is "enter" (13)
e.preventDefault(); // prevent default "enter" action
addItem(); // addItem function is invoked
};
});

function clearList(){
var myList = document.getElementById("list"); // get the "ul" element
myList.innerHTML = ""; // clear all it's children
window.localStorage.clear();
};
/* deleteAll variable reffers the button used to clear all items. After clicking it, function "deleteAll" is called */
var deleteAll = document.getElementById("deleteAll"); // fetch the "deleteAll" button
deleteAll.addEventListener("click", clearList); // add click event to "deleteAll" button and run finction

/* This code changes the class of clicked "li" element from "item" to "itemDone" using event delegation method */
document.getElementById("list").addEventListener("click", function(e) {
if (e.target && e.target.matches("li.item")) { // if event target matches an "li" item with "item" class
e.target.className = "itemDone"; // change class to "itemDone"
var check = document.createElement("i"); // create new "i" element
check.className = "fa fa-check done"; // add class to "i" element
e.target.appendChild(check); // add new "i" element to event target ("li")
} else if (e.target && e.target.matches("li.itemDone")) {// if event target matches an "li" item with "itemDone" class
e.target.className = "item"; // change class to "item"
var done = e.target.getElementsByClassName("done"); // choose target with "done" class
for (var i = 0; i < done.length; i++) {
e.target.removeChild(done[i]); // remove "i" element with "done" class from target
};
};
});

/* Save all "li" items form the list to localStorage */
var saveAll = document.getElementById("saveAll");
saveAll.addEventListener("click", function() {
var item = document.getElementsByTagName("li");
for (var i = 0; i < item.length; i++) {
localStorage.setItem([i], JSON.stringify(item[i].innerHTML));
};
});
})();


And HTML:

<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Sansita&amp;subset=latin-ext">
<link rel="stylesheet" href="css/font-awesome.min.css">
<link rel="stylesheet" href="css/main.css">
<title>Prosta aplikacja To-Do list</title>
<meta name="description" content="Prosta aplikacja zadań To-Do">
<meta name="keywords" content="aplikacja, to-do, lista, zadań, javascript">
<meta name="author" content="Michał Grochowski">
</head>
<body>
<header id="header">
<div class="description">
<h1>Prosta aplikacja To-Do</h1>
</div>
</header>
<section>
<div class="mainlist">
<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>
<div class="flex-row">
<button id="deleteAll" type="button">Wyczyść</button>
<button id="saveAll" type="button">Zapisz</button>
</div>
<p class="helpText">Żeby dodać pozycję do listy wpisz tekst w polu "Dodaj pozycję", a następnie kliknij przycisk "Dodaj" lub wciśnij enter.</p>
<p class="helpText">Kliknij wybrane zadanie, żeby oznaczyć je jako wykonane. W ten sam sposób możesz je odznaczyć.</p>
<p class="helpText">Usuń wybraną pozycję z listy kilkając "X" w prawym rogu.</p>
<p class="helpText">Żeby zapisac listę w pamięci przeglądarki, kliknij przycisk "Zapisz".</p>
<p class="helpText">Żeby wyczyścić całą listę, kliknij przycisk "Wyczyść".</p>
</div>
</section>
<footer>
<div class="footer">
<p>&copy 2017 <a href="http://dobrywebdev.pl" target="_blank">Michał Grochowski - Dobrywebdev.pl</a></p>
</div>
</footer>
<script src="js/script.js"></script>
</body>
</html>


I know that it's not perfect and I will gladly accept any criticism of above code, but mostly I want to solve that localStorage issue.

Answer Source

Problem is with variable i, specifically this for loop:

var closers = document.getElementsByClassName("closer");
    for (var i = 0; i < closers.length; i++) {
         closers[i].addEventListener("click", deleteItem);
    }

You’ve already declared i in the for loop above, and this loop is a duplicate of the loop below that deletes an item. I’ve deleted that loop, also I’ve putted all your code inside DOMContentLoaded event, so it will be available when the initial HTML has been completely loaded and parsed.

Note that in Firefox the order of localStorage keys is reversed, so list order will be reversed as well, in Chrome it will work as intended. And you should declare localKey with var keyword. Hope it will help.

// Not needed, but it helps, like with localKey declaration.
"use strict";

(function() {
/* Load items from localStorage if there are any */
window.addEventListener("DOMContentLoaded", function() {
    var myList = document.getElementById("list");

    for (var i = 0; i < localStorage.length; i++) {
        var item = document.createElement("li");
        var localKey = localStorage.getItem(localStorage.key(i));
        item.innerHTML = JSON.parse(localKey);
        if (item.innerHTML.indexOf("check") != -1) {
            item.className = "itemDone";
        } else {
            item.className = "item";
        }
        myList.appendChild(item);
    };

    /* This function deletes parent element of clicked element - in this case a clicked element will be a "span" element with "closer" class, and it's parent is "li" element */
    function deleteItem() {
        this.parentNode.parentNode.removeChild(this.parentNode);
    };

    // TODO: Not a function, wasn't working because all code needed to be inside addEventListener("DOMContentLoaded", like now.
    /* this function allows to delete item from the loist */
    var x = document.getElementsByClassName("closer"); // get all elements with "closer" class

    for (var i = 0; i < x.length; i++) { // simple loop that adds eventListener to all elements with "closer" class
        x[i].addEventListener("click", deleteItem); // on click function "deleteItem" is invoked
    };

    /* This function adds "li" element to the list with text value from input #newInput, and then adds a span with innertext "X" and class .closer */
    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
        newListItem.className = ""
        if (itemText === "") { // if input value 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
            x.addEventListener("click", deleteItem, false);
            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 value to null
        }
    };
    /* addButton reffers to the button used to add new elements to the list. After clicking it, function "addItem" is called */
    var addButton = document.getElementById("createNew"); // fetch the "createNew" button
    addButton.addEventListener("click", addItem); // add click event to "createNew" button and run function
    /* This function clears all added elements from the list */

    /* This script adds a method in which you can also add a new "li" item with "enter" keydown */
    var textAdd = document.getElementById("newInput"); // target input
    textAdd.addEventListener ("keypress", function (e) { // add event listener on "keypress"
        var key = e.which || e.keyCode; // which key is being used
        if (key === 13){ // if pressed key is "enter" (13)
        e.preventDefault(); // prevent default "enter" action
        addItem(); // addItem function is invoked
        };
    });

    function clearList(){
        var myList = document.getElementById("list"); // get the "ul" element
        myList.innerHTML = ""; // clear all it's children
        window.localStorage.clear();
    };
    /* deleteAll variable reffers the button used to clear all items. After clicking it, function "deleteAll" is called */
    var deleteAll = document.getElementById("deleteAll"); // fetch the "deleteAll" button
    deleteAll.addEventListener("click", clearList); // add click event to "deleteAll" button and run finction

    /* This code changes the class of clicked "li" element from "item" to "itemDone" using event delegation method */
    document.getElementById("list").addEventListener("click", function(e) {
        if (e.target && e.target.matches("li.item")) { // if event target matches an "li" item with "item" class
            e.target.className = "itemDone"; // change class to "itemDone"
            var check = document.createElement("i"); // create new "i" element
            check.className = "fa fa-check done"; // add class to "i" element
            e.target.appendChild(check); // add new "i" element to event target ("li")
        } else if (e.target && e.target.matches("li.itemDone")) {// if event target matches an "li" item with "itemDone" class
        e.target.className = "item"; // change class to "item"
            var done = e.target.getElementsByClassName("done"); // choose target with "done" class
            for (var i = 0; i < done.length; i++) {
            e.target.removeChild(done[i]); // remove "i" element with "done" class from target
            };
        };
    });

    /* Save all "li" items form the list to localStorage */
    var saveAll = document.getElementById("saveAll");
    saveAll.addEventListener("click", function() {
        var item = document.getElementsByTagName("li");
        for (var i = 0; i < item.length; i++) {
            localStorage.setItem([i], JSON.stringify(item[i].innerHTML));
        };
    });
});
})();
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download