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", "[email protected]");
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 = "[email protected]"
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();
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.
Hi Daniel,
I tested on the Firefox browser and it was working fine. But I can notice in the checkbox size only that they are small in comparison to Chrome. Rest is fine.
Thank you for your quick response. It must be something on my machine then 🙂
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? 😀
The above JavaScript code writing li elements as new items to the object id todoListView so if you want some item predefined then you have to create some li elements. Following is an example:
Hi Vinish,
Great work, looks really nice. Where in the code do you place the predetermined items? I've tired in a few places and it either doesn't come up at all or they come up as bullet points and not checkboxes.
Thanks