Press "Enter" to skip to content

JavaScript: Local Storage with a Practical Todo List Example

HTML5 provides the localStorage and sessionStorage objects that let the web page use JavaScript to store data in name and value pairs. In this tutorial, I will show you how to use the local storage using JavaScript with a practical 🔥Todo List example.

JavaScript localStorage Object Methods and Syntax

The localStorage is retained indefinitely.

localStorage.setItem("itemname", "value") // saves the data in the item
localStorage.getItem("itemname")          // get the data in the item
localStorage.removeItem("itemname")       // removes the item
localStorage.clear()                      // removes all items

JavaScript sessionStorage Object Methods and Syntax

The sessionStorage is lost when the user closes the browser.

sessionStorage.setItem("itemname", "value") // saves the data in the item
sessionStorage.getItem("itemname")          // get the data in the item
sessionStorage.removeItem("itemname")       // removes the item
sessionStorage.clear()                      // removes all items

The shortcut syntax for getting or saving an item

localStorage.itemname                       // saves or gets the data
sessionStorage.itemname                     // saves or gets the data

setItem Method

The setItem method requires two parameters that provide the name of an item and the value for the item. For instance, you can use code like this to add items named “email” and “phone” that store the email address and phone number of the user:

localStorage.setitem("email", "abc@gmail.com");
localStorage.setitem("phone", "+91 111222333");

getItem Method

Now you can use the getItem method with the item name as the parameter to retrieve the data for the phone item with the statement as follows:

localStorage.getItem("phone");

To simplify, you can use the shortcut syntax as shown in the below example:

localStorage.email = "abc@gmail.com"
localStorage.phone = "+91 111222333"

An example to get the local storage value into the variable:

var phone = localStorage.phone;

Todo List Example

Here I am giving an example of a beautiful Todo list created using the JavaScript localStorage object:

HTML Code

<html lang="en">

<head>
  <meta charset="utf-8">
  <title>Todo List</title>
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <section class="todoapp">
    <header class="header">
      <h1>todo list</h1>
      <input id="newTodo" class="new-todo" placeholder="What needs to be done?">
    </header>
    <section id="main">
      <input id="toggleInputAll" class="toggle-all" type="checkbox">
      <label for="toggle-all">Mark all as complete</label>
      <ul id="todoListView" class="todo-list"></ul>
    </section>
    <footer class="footer">
      <span class="todo-count"><strong id="todoCount">0</strong> item left</span>
      <ul class="filters">
        <li>
          <a class="selected" id="allWorks" onclick="changeClass(this)" href="#/">All</a>
        </li>
        <li>
          <a href="#active" id="activedItems" onclick="changeClass(this)">Active</a>
        </li>
        <li>
          <a href="#completed" id="completedTodos" onclick="changeClass(this)">Completed</a>
        </li>
      </ul>
      <button class="clear-completed" id="btnClear">Clear completed</button>
    </footer>
  </section>

  <script src="todo-list.js"></script>
</body>

</html>

CSS Code

html,
body {
margin: 0;
padding: 0;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
font-weight: inherit;
color: inherit;
}
button, input[type="checkbox"] {
outline: none;
}
body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #f5f5f5;
color: #4d4d4d;
min-width: 230px;
max-width: 550px;
margin: 0 auto;
font-weight: 300;
}
.hidden {
display: none;
}
.todoapp {
background: #fff;
margin: 130px 0 40px 0;
position: relative;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.todoapp input::input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp h1 {
position: absolute;
top: -155px;
width: 100%;
font-size: 100px;
font-weight: 100;
text-align: center;
text-transform: capitalize;
color: #cc9a9a;
}
.new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
border: 0;
outline: none;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
box-sizing: border-box;
}
.new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.003);
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
}
.main {
position: relative;
z-index: 2;
border-top: 1px solid #e6e6e6;
}
label[for='toggle-all'] {
display: none;
}
.toggle-all:before {
content: '❯';
font-size: 22px;
color: #e6e6e6;
padding: 10px 27px 10px 27px;
}
.toggle-all:checked:before {
color: #737373;
}
.toggle-all {
position: absolute;
top: 5px;
left: -12px;
width: 60px;
height: 34px;
text-align: center;
border: none; /* Mobile Safari */
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-appearance: none;
}
.todo-list {
margin: 0;
padding: 0;
list-style: none;
}
.todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px solid #ededed;
}
.todo-list li:last-child {
border-bottom: none;
}
.todo-list li.checked {
color: #979797;
font-weight: normal;
text-decoration: line-through;
}
.todo-list li.checked input[type="checkbox"]:after {
border: 1px solid #166B94;
border-radius: 3px;
color: #fff;
content: "";
display: block;
height: 16px;
line-height: 16px;
position: absolute;
text-align: center;
visibility: visible;
width: 16px;
}
.todo-list li.checked input[type=checkbox]:checked:after  {
border: 1px solid #979797;
color: #979797;
content: "✓";
font-size: 25px;
color: green;
}
.todo-list li .itemList {
text-align: center;
height: 20px;
/* auto, since non-WebKit browsers doesn't support input styling */
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
}
.todo-list li label {
white-space: pre-line;
word-break: break-all;
padding: 15px 60px 15px 15px;
margin-left: 45px;
display: block;
line-height: 1.2;
transition: color 0.4s;
}
.todo-list input[type=checkbox] {
cursor: pointer;
visibility: hidden;
margin-left: 20px;
}
.todo-list input[type="checkbox"]:after {
border: 1px solid #166B94;
border-radius: 3px;
color: #fff;
content: "";
display: block;
height: 16px;
line-height: 16px;
position: absolute;
text-align: center;
visibility: visible;
width: 16px;
}
.todo-list input[type=checkbox]:checked:after {
border: 1px solid #979797;
color: #979797;
content: "✓";
font-size: 25px;
color: green;
}
.todo-list input[type=checkbox]:checked + label {
color: #979797;
font-weight: normal;
text-decoration: line-through;
}
.todo-list li .remove {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 30px;
color: #cc9a9a;
margin-bottom: 11px;
transition: color 0.2s ease-out;
cursor: pointer;
}
.todo-list li .remove:hover {
color: #af5b5e;
}
.todo-list li .remove:after {
content: '×';
}
.todo-list li:hover .remove {
display: block;
}
.edit {
display: none;
}
li.editing {
display: block;
width: 430px;
}
li.editing > label {
display: none;
}
li.editing > input.edit {
display: block !important;
}
li:hover.editing > button.remove {
display: none ;
}
.todo-list li.editing .edit {
display: block;
width: 506px;
padding: 13px 17px 12px 17px;
margin: 0 0 0 43px;
}
.show-all {
display: block;
}
.todo-list li.editing:last-child {
margin-bottom: -1px;
}
.footer {
color: #777;
padding: 10px 15px;
height: 20px;
text-align: center;
border-top: 1px solid #e6e6e6;
}
.footer:before {
content: '';
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 50px;
overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
0 8px 0 -3px #f6f6f6,
0 9px 1px -3px rgba(0, 0, 0, 0.2),
0 16px 0 -6px #f6f6f6,
0 17px 2px -6px rgba(0, 0, 0, 0.2);
}
.todo-count {
float: left;
text-align: left;
}
.filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: 0;
}
.filters li {
display: inline;
}
.filters li a {
color: inherit;
margin: 3px;
padding: 3px 7px;
text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
}
.filters li a.selected {
border-color: rgba(175, 47, 47, 0.2);
}
.clear-completed, html .clear-completed:active {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
cursor: pointer;
}
.clear-completed:hover {
text-decoration: underline;
}

JavaScript Code

'use strict'
function Todo(id, content, isDone) {
this.id = id;
this.content = content;
this.isDone = isDone;
};
/**
* Declare a controller
*/
function TodoController() {
this.todoList = [];
this.id = 1;
this.ENTER_KEY = 13;
this.todoInput = document.getElementById('newTodo');
this.todoListView = document.getElementById('todoListView');
};
TodoController.prototype = {
/**
* @param {argument} key - get to localstorage
*/
getTodoFromLocalstorage: function (key) {
var todoList = JSON.parse(localStorage.getItem(key)) || [];
return todoList;
},
/**
* @param {argument} key - set into localstorage
*/
setTodoLocalstorage: function (key) {
localStorage.setItem('todoList', JSON.stringify(key));
},
/**
* @param {sting} value - content todo
*/
handleTodoItem: function (value) {
this.isDone = false;
var mainArray = todoController.getTodoFromLocalstorage('todoList');
this.id = todoController.idLargestOfLocal(mainArray) + 1;
var todoItem = new Todo(this.id, value, this.isDone);
return todoItem;
},
/**
* @param {array} mainArray - find id last in array at localstorage
*/
idLargestOfLocal: function (mainArray) {
var lengthArr = mainArray.length;
if (lengthArr !== 0) {
return mainArray[lengthArr - 1].id;
} else {
return 0;
}
return lastId;
},
/**
* Presentation create new a todo item
* @param {array} list - id for todo
* @return {object} todo - return todo object
*/
addNewTodo: function (todo, list) {
list.push(todo);
todoController.setTodoLocalstorage(list);
return todo;
},
/**
* Presentation create new a todo item
* @param {value attribute} attrs - value attribute for element html 
* @return {attribute} element - attribute for element html
*/
setAttributes: function (element, attrs) {
for (var key in attrs) {
element.setAttribute(key, attrs[key]);
}
},
/**
* Create new checkbox input element
* @param {number} todoId - id checkbox
*/
checkboxView: function (todoId) {
var inpCheckbox = document.createElement('input');
this.setAttributes(inpCheckbox, { type: 'checkbox', class: 'itemList', id: todoId });
//event check for input checkbox
inpCheckbox.addEventListener('click', function (e) {
//get list array from localStorage
var list = todoController.getTodoFromLocalstorage('todoList');
var id = e.target.getAttribute('id');
for (var i = 0; i < list.length; i++) {
if (list[i].id == id) {
list[i].isDone = e.target.checked;
}
}
//save list todo to localStorage
todoController.setTodoLocalstorage(list);
todoController.countItem();
});
return inpCheckbox;
},
/**
* Create new lable element
* @param {object} todo - item todo from addNewTodo 
*/
createLableView: function (todo) {
var lbContent = document.createElement('label');
this.setAttributes(lbContent, { value: todo.content, class: 'labelContent ' });
lbContent.innerHTML = todo.content;
//return node lable
return lbContent;
},
/**
* Create new li element
* @param {object} todo - item todo from addNewTodo 
*/
initTodoITem: function (todo) {
var item = document.createElement('li');
item.setAttribute('class', 'todoItem');
// this.setAttributes(item, { , class: 'todoItem ' });
//event event double click in node li
item.addEventListener('dblclick', function (e){
item.classList.add('editing');
});
//return node li
return item;
},
/**
* Create new input edit element
* @param {object} todo - item todo from addNewTodo 
*/
editInputView: function (todo) {
//get array from localStorage
var list = todoController.getTodoFromLocalstorage('todoList');
var inputEdit = document.createElement('input');
this.setAttributes(inputEdit, {
id: todo.id,
class: 'edit',
value: todo.content,
type: 'text',
});
inputEdit.focus();
//event onblur get value edit and delete class editing when click outside this input
inputEdit.onblur = function (e) {
todoController.handleTodoUpdate(e);
};
//event onkeyup get value edit form inputEdit
inputEdit.onkeypress = function (e) {
if (event.which == todoController.ENTER_KEY || event.keyCode == todoController.ENTER_KEY) {
todoController.handleTodoUpdate(e);
}
};
//return node input for edit todo
return inputEdit;
},
handleTodoUpdate: function (event) {
var list = todoController.getTodoFromLocalstorage('todoList');
var inputEdit = event.target;
var todoItem = new Todo(inputEdit.id, inputEdit.value, false);
todoController.updateTodoEdit(todoItem, list);
var editing = document.querySelector('.editing');
editing.classList.remove('editing');
todoController.renderTodo();
},
/**
* Presentation update todo edit
* @param {object} todo - get item todo from event get value edit
* @param {array} list - array in localStorage
*/
updateTodoEdit: function (todo, list) {
for (var i = 0; i < list.length; i++) {
if (list[i].id == todo.id) {
list[i].content = todo.content;
todoController.setTodoLocalstorage(list);
break;
}
}
//return new object have edit
return todo;
},
/**
* Presentation create new button remove item todo
* @param {object} todo - get item todo from event get value edit
*/
removeButtonView: function (todo) {
var btnRemove = document.createElement('button');
this.setAttributes(btnRemove, { class: 'remove', id: todo.id });
//event click mouse into btnRemove a item todo
btnRemove.addEventListener('click', function (e) {
var id = e.target.getAttribute('id');
todoController.removeTodo(id);
todoController.renderTodo();
todoController.countItem();
});
//return node button
return btnRemove;
},
/**
* Presentation create new a todo item
* @param {object} todo - object render to view 
*/
todoView: function (todo) {
var item = this.initTodoITem(todo);//create node li
var inpCheckbox = this.checkboxView(todo.id),//create node input checkbox
lbContent = this.createLableView(todo),//create node lable
inputEdit = this.editInputView(todo),//create node input edit
btnRemove = this.removeButtonView(todo);//create node button remove item todo
//item append each element
item.appendChild(inpCheckbox);
item.appendChild(lbContent);
item.appendChild(inputEdit);
item.appendChild(btnRemove);
//ul append each item
document.querySelector('#todoListView').appendChild(item);
//return node li contain inpCheckbox, lbContent, inputEdit, btnRemove
return item;
},
/**
* Presentation remove a item todo
* @param {number} id - id button remove item todo
* @param {array} list - list array get from localStorage
*/
removeTodo: function (id, list) {
list = todoController.getTodoFromLocalstorage('todoList');
for (var i = 0; i < list.length; i++)  {
if (list[i].id == id) {
list.splice(i, 1);
break;
}
}
//set value after remove item to localStorage
todoController.setTodoLocalstorage(list);
},
/**
* Presentation remove a item todo
* @param {index} index - index in array object 
* @param {array} list - list array get from localStorage
*/
countItem: function (index, list) {
list = todoController.getTodoFromLocalstorage('todoList');
index = 0;
for (var i = 0; i < list.length; i++) {
if (!list[i].isDone) {
index++;
}
}
// return index display to UI;
document.getElementById('todoCount').innerHTML = index;
},
/**
* Presentation the events for todo
*/
events: function () {
// Event add todo
todoController.todoInput.onkeyup = function (event) {
if (event.which == todoController.ENTER_KEY || event.keyCode == todoController.ENTER_KEY) {
//get from localStorage
var todoList = todoController.getTodoFromLocalstorage('todoList');
//attach value for todo
var todoItem = todoController.handleTodoItem(todoController.todoInput.value);
//add new a Todo
var todo = todoController.addNewTodo(todoItem, todoList);
//Execute display to UI
todoController.todoView(todo);
//clear input
todoController.todoInput.value = '';
todoController.countItem();
}
};
//event check all checkbox in list item
var list = document.getElementsByClassName('itemList');
var checkAll = document.getElementById('toggleInputAll');
checkAll.addEventListener('change', function (e) {
var check;
for (var i = 0; i < list.length; i++) {
list[i].checked = this.checked;
check = e.target.checked;
todoController.checkAllTodo(check);
}
todoController.countItem();
});
//Show all items
var listWork = document.getElementsByClassName('todoItem');
var showAllItem = document.getElementById('allWorks');
showAllItem.addEventListener('click', function () {
for (var i = 0; i < listWork.length; i++) {
listWork[i].style.display = 'block';
}
});
// Filter todo list with actived items
var activeItem = document.getElementsByClassName('todoItem');
var todoActive = document.getElementById('activedItems');
todoActive.addEventListener('click', function () {
for (var i = 0; i < list.length; i++) {
if (!list[i].checked) {
activeItem[i].style.display = 'block';
} else {
activeItem[i].style.display = 'none';
}
}
});
//Filter completed todo list
var completeItem = document.getElementsByClassName('todoItem');
var todoCompleted = document.getElementById('completedTodos');
todoCompleted.addEventListener('click', function () {
for (var i = 0; i < list.length; i++) {
if (list[i].checked) {
completeItem[i].style.display = 'block';
} else {
completeItem[i].style.display = 'none';
}
}
});
// Added event clear completed items for button
var clearButton = document.getElementById('btnClear');
clearButton.addEventListener('click', function () {
//get from localStorage
var list = todoController.getTodoFromLocalstorage('todoList');
todoController.clearCompleted(list);
todoController.setTodoLocalstorage(list);
todoController.renderTodo();
});
},
/**
* Presentation clear all item todo have isDone
* @param {array} list - get from localstorage
*/
clearCompleted: function (list) {
while (list.find(({ isDone }) => isDone)) {
list.splice(list.indexOf(list.find(({ isDone }) => isDone)), 1);
}
},
/**
* Presentation set status isDone into localstorage
* @param {boolean} check - isDone from event checkall 
* @param {array} todoList - list array get from localStorage
*/
checkAllTodo: function (check, todoList) {
todoList = todoController.getTodoFromLocalstorage('todoList');
for (var i = 0; i < todoList.length; i++) {
todoList[i].isDone = check;
todoController.setTodoLocalstorage(todoList);
}
},
/**
* Presentation set status isDone into localstorage
* @param {array} list - list array get from localStorage
*/
renderTodo: function () {
//get from localStorage
var list = todoController.getTodoFromLocalstorage('todoList');
todoController.removeElement();
for (var i = 0; i < list.length; i++) {
var element = todoController.todoView(list[i]);
if (list[i].isDone) {
element.classList.add('checked');
}
}
},
removeElement: function () {
var todoListView = document.getElementById('todoListView');
while (todoListView.hasChildNodes()) {
todoListView.removeChild(todoListView.firstChild);
}
},
};
//change class selected
function changeClass(elem) {
var a = document.getElementsByTagName('a');
for (var i = 0; i < a.length; i++) {
a[i].classList.remove('selected');
};
//add class selected for element user click
elem.classList.add('selected');
};
//todoController handle all action add, delete, edit, events
var todoController = new TodoController();
//todo create new object todo
var todo = new Todo();
//performing the events
todoController.events();
// //performing render todo display to UI
todoController.renderTodo();
//performing count all item active
todoController.countItem();

Output

JavaScript local storage example with a Todo list demo.

Related Tutorials:

Reference:

Have you found the answer to your question? If not, you can discuss it with me in the comments section below or join my Q&A community OrclQA.com for developers and ask your question. It is FREE.

Vinish Kapoor

Follow

Hi, I am a full stack developer and writing about development. I document everything I learn and help thousands of people. foxinfotech.in is created, written, and maintained by me; it is built on WordPress, and hosted by Bluehost. Connect with me on Facebook, Twitter, GitHub, and get notifications for new posts.

guest
5 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Daniel Larsen
Daniel Larsen
19 days ago

Wow, great work! 😀

I hope you can help with a little Firefox issue:
The checkbox vanishes when running in firefox (78.0.2 (64-bit)), but works fine in chromium browsers and I can’t seem to locate the problem.

Daniel Larsen
Daniel Larsen
Reply to  Vinish Kapoor
18 days ago

Thank you for your quick response. It must be something on my machine then 🙂

Daniel Larsen
Daniel Larsen
18 days ago

Sorry to bother you again, but I was wondering if it is possible to create premade items? And if so, can you please tell me how? 😀