Hello, and welcome to our journey about todo lists! In this lesson, we delve into the world of Data Persistence and Local Storage. We're going to figure out how to store our todo items right on our own machine, specifically in the web browser. By the end of this lesson, you'll be well-equipped to store and retrieve data even after the browser is closed and reopened. This feature gives the user freedom from the fear of losing their data accidentally. It's as if we're creating our own mini-database right in our web browser. So, let's plow ahead and get on with this exciting journey!
Local storage is a type of web storage that allows JavaScript websites and apps to store and access data right in the browser with no expiration time. It's like a small database that lives in your browser. Even after closing and reopening the browser, the data persists, giving us a form of data persistence.
Can you think of examples where this could prove useful? Imagine you're filling out a lengthy form and accidentally refresh the page. Without local storage, all the information you've filled out would be lost. But if the website cleverly stored this data in local storage as you were filling it out, it could conveniently recover it, recovering your data and saving you time. Local storage could make this clever recovery possible. Now let's see how we can use it to store our todo items.
First, let's look at the local storage toolbox. There are three primary methods that are of utmost importance in using local storage: setItem
, getItem
, and removeItem
.
At its core, local storage is like a box with many compartments, and we store data in these compartments. But to fetch the data or remove it when it's no longer needed, we need some tools, right? That's what these methods essentially are — our tools to work with local storage.
-
setItem
method: This is used to put data into local storage. It's like putting a thing into a compartment of our box. It requires two arguments: a key and a value. The key is the name of the compartment, and the value is the thing itself. Let's put something into our local storage:JavaScript1localStorage.setItem('favoriteColor', 'Blue');
Here,
favoriteColor
is the key, andBlue
is the value. So, we've storedBlue
into a compartment namedfavoriteColor
. -
getItem
method: Once our data is in local storage, we need a way to retrieve it. That's whatgetItem
does. The key is required as an argument to access the compartment, as shown below:JavaScript1const myFavoriteColor = localStorage.getItem('favoriteColor'); 2console.log(myFavoriteColor); // Displays Blue on the console
Here, we are fetching data from the compartment named
favoriteColor
. So,myFavoriteColor
will hold the valueBlue
. -
removeItem
method: Sometimes, we no longer need the data we put in local storage, so we need to remove it. This is done usingremoveItem
.JavaScript1localStorage.removeItem('favoriteColor');
We can see that
favoriteColor
is removed from local storage. If you try to getfavoriteColor
now, it will returnnull
because it's not in local storage anymore.
In our todo application, we have a function called saveTodos
. Its role is like a very efficient librarian who checks the todos (like books in a library), collects them all, and puts them nicely into local storage (like a library's shelves). Every time a change is made to our todos, like adding a new todo or marking a todo as completed, the librarian immediately updates the storage, ensuring the correct state of todos is maintained.
Here's the code for the saveTodos
function:
JavaScript1function saveTodos() { 2 const todos = []; 3 document.querySelectorAll('#todo-list li').forEach(li => { 4 const todoText = li.childNodes[1].nodeValue; 5 const isChecked = li.childNodes[0].checked; 6 todos.push({ text: todoText, checked: isChecked }); 7 }); 8 localStorage.setItem('todos', JSON.stringify(todos)); 9}
Let's go through it step-by-step:
- We define an empty array
todos
. - We use
document.querySelectorAll('#todo-list li')
to get all the list items (i.e., our todos), then use theforEach
method to loop through each item. - For each item, we get its text content and whether its checkbox is checked. We then create an object representing our todo and add it to our
todos
array. - Finally, we call
localStorage.setItem
to store thetodos
array in local storage. Since local storage can only store strings, we useJSON.stringify(todos)
to convert the array into a string format.
To see the stored todos in your console, you can use the getItem
method to fetch the 'todos' item from local storage. This will return the stored string representation of the todos' array, which you can then log to the console to inspect:
JavaScript1console.log(localStorage.getItem('todos'));
When you run this, you'll see the stringified array of todos:
JSON1[{"text":"Item 1","checked":false},{"text":"Item 2","checked":true},{"text":"Item 3","checked":false}]
This output shows that we have three todos. The first todo ("Item 1") is not checked, the second todo ("Item 2") is checked, and the third todo ("Item 3") is not checked. By logging the value stored in local storage, we can verify that our todos are being saved correctly.
In certain scenarios, it's vital to save the state frequently, even after every single change — whether adding, deleting, moving a todo, or updating a checkbox. This frequent saving ensures that our local storage is always up-to-date. Imagine if the browser closes unexpectedly; without saving each change, those unsaved changes would be lost. By constantly updating local storage, we guarantee that our todo list will always return to its correct state, even after the browser is closed and reopened. Think of it as continually updating a diary to avoid losing track of your tasks.
Our loadTodos
function is like the opposite of saveTodos
. It's our trusted librarian who, when the library opens, gets all the books (i.e., todos) from the shelves (i.e., local storage) and arranges them for viewing. The function code is as follows:
JavaScript1function loadTodos() { 2 const todos = JSON.parse(localStorage.getItem('todos')); 3 if (todos) { 4 todos.forEach(todo => { 5 const li = document.createElement('li'); 6 const checkbox = document.createElement('input'); 7 checkbox.type = 'checkbox'; 8 checkbox.checked = todo.checked; 9 checkbox.addEventListener('change', function() { 10 if (checkbox.checked) { 11 li.style.textDecoration = 'line-through'; 12 } else { 13 li.style.textDecoration = 'none'; 14 } 15 saveTodos(); 16 }); 17 18 li.appendChild(checkbox); 19 li.appendChild(document.createTextNode(todo.text)); 20 21 document.getElementById('todo-list').appendChild(li); 22 23 if (todo.checked) { 24 li.style.textDecoration = 'line-through'; 25 } 26 }); 27 } 28}
Here, we are fetching our todos from local storage using localStorage.getItem
. But why are we surrounding it with JSON.parse
? Well, remember that we stored our todos as a string with JSON.stringify
. Now, we need to turn it back into an array of objects we can use in JavaScript, and JSON.parse
does exactly that. The rest of the code within the function body creates li
elements just like before, with all the event listeners, and adds them to the todo list in the same arrangement they were stored in.
Loading todos wouldn't be very useful if it only worked once — what about when the page is reloaded, or when we open our webpage the next day?
JavaScript1document.addEventListener('DOMContentLoaded', loadTodos);
This line of code ensures that our todos are loaded every time our webpage fully loads, bringing our todos back to life. Without DOMContentLoaded
, our todo list would be empty every time we reloaded the page, which wouldn't be very useful, would it?
Now let's take all the pieces we've discussed and put them together into a cohesive whole. We'll create a simple todo list application that can add, display, and save todos, allowing us to see exactly how local storage can be implemented in a real-world scenario.
- Adding a New Todo: We'll use an input field and a button to add new todos to the list.
- Saving Todos: Each new todo will be saved in local storage so that it persists across browser sessions.
- Loading Todos: When the page is loaded, todos from local storage will be displayed.
- Toggling Complete Status: Each todo will have a checkbox to mark it as complete, and its status will be stored in local storage.
Here's the full code to implement this functionality:
JavaScript1document.getElementById('add-btn').addEventListener('click', function() { 2 const newTodo = document.getElementById('new-todo').value; 3 if (newTodo.trim() === '') return; 4 5 const li = document.createElement('li'); 6 const checkbox = document.createElement('input'); 7 checkbox.type = 'checkbox'; 8 checkbox.addEventListener('change', function() { 9 if (checkbox.checked) { 10 li.style.textDecoration = 'line-through'; 11 } else { 12 li.style.textDecoration = 'none'; 13 } 14 saveTodos(); 15 }); 16 17 li.appendChild(checkbox); 18 li.appendChild(document.createTextNode(newTodo)); 19 20 document.getElementById('todo-list').appendChild(li); 21 document.getElementById('new-todo').value = ''; 22 23 saveTodos(); 24}); 25 26function saveTodos() { 27 const todos = []; 28 document.querySelectorAll('#todo-list li').forEach(li => { 29 const todoText = li.childNodes[1].nodeValue; 30 const isChecked = li.childNodes[0].checked; 31 todos.push({ text: todoText, checked: isChecked }); 32 }); 33 localStorage.setItem('todos', JSON.stringify(todos)); 34} 35 36function loadTodos() { 37 const todos = JSON.parse(localStorage.getItem('todos')); 38 if (todos) { 39 todos.forEach(todo => { 40 const li = document.createElement('li'); 41 const checkbox = document.createElement('input'); 42 checkbox.type = 'checkbox'; 43 checkbox.checked = todo.checked; 44 checkbox.addEventListener('change', function() { 45 if (checkbox.checked) { 46 li.style.textDecoration = 'line-through'; 47 } else { 48 li.style.textDecoration = 'none'; 49 } 50 saveTodos(); 51 }); 52 53 li.appendChild(checkbox); 54 li.appendChild(document.createTextNode(todo.text)); 55 56 document.getElementById('todo-list').appendChild(li); 57 58 if (todo.checked) { 59 li.style.textDecoration = 'line-through'; 60 } 61 }); 62 } 63} 64 65document.addEventListener('DOMContentLoaded', loadTodos);
This example provides a comprehensive demonstration of how local storage can be used to enhance a todo list application by preserving data between sessions.
While local storage is very useful, keep in mind that it has a storage limit (usually around 5-10 MB per domain). Exceeding this limit could result in data not being saved. It's important to monitor the data size stored to avoid hitting this limit, especially when dealing with large applications.
Congratulations! You've unlocked another exciting aspect of JavaScript — Local Storage! Now, you're equipped with the tools to store and retrieve data, ensuring that it persists even if the browser is closed and re-opened.
We've discussed the local storage concept and its methods, setItem
, getItem
, and removeItem
. We also delved into saveTodos()
and loadTodos()
, our methods for saving todos to local storage and loading them from there. Importantly, we learned why it’s crucial to save todos after each change and touched upon the local storage capacity limits.
Next up, you'll get a chance to apply your newfound knowledge in practice exercises! Remember, exercises are an integral part of learning programming. You’ve got this! Happy coding!