The website is coming quite nicely, but now I understand what devs fought against when I used to be on the other side of the conversation asking about a specific project or why a concept was taking so long, it really takes some time to develop code from nothing - or maybe that's just my perception because I am still in diapers regarding coding.
In the meantime, I leave you with yet another little project that helps me keep grinding my skills and at the same time, build another layer of my soon to be portfolio.
ToDo List
I am going to create a To-Do list with a nice design. The UI will be simple and intuitive. I will implement local storage for this little app, and the list will be interactive so the user can manipulate the elements.
HTML Code
<body>
<h1>To Do List</h1>
<form id="form">
<input
type="text"
class="input"
id="input"
placeholder="Enter your ToDo"
autocomplete="off"
/>
<ul class="todos" id="todos">
(html comment removed: <li>Sample item</li>
<li>Another Sample</li>
<li>To have something</li>
<li>To style on CSS</li>
<li class="completed">I'll delete this later</li>
<li>Cause the JS will</li>
<li>Handle this dynamically</li> )
</ul>
</form>
<small
>Left click to complete item <br />
Right click to delete item
</small>
<script src="script.js"></script>
</body>
</html>
CSS Code
* {
box-sizing: border-box;
}
body {
background-color: aliceblue;
color: #445;
font-family: "Poppins", sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
}
This is mostly a visual mini project, so let's style this freaking awesomely.
h1 {
color: rgb(38, 26, 214);
font-size: 10rem;
text-align: center;
opacity: 0.7;
}
form {
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
max-width: 100%;
width: 400px;
}
.input {
border: none;
color: #445;
font-size: 2rem;
padding: 1rem 2rem;
display: block;
width: 100%;
}
.input::placeholder {
opacity: 0.3;
}
.todos {
background-color: #f8f7f2;
padding: 1rem;
list-style-type: none;
margin: 0;
}
.todos li {
border-top: #e5e5e5;
margin: 0.5rem 0;
cursor: pointer;
font-size: 1.5rem;
}
.todos li.completed {
color: #b1b1b1;
text-decoration: line-through;
}
small {
margin-top: 2rem;
text-align: center;
font-size: 1rem;
}
.input:focus {
outline-color: rgb(38, 26, 214);
}
JavaScript Code
First I'll add the DOM functionalities and then I'll do the local storage. This means I care more about making the JS work in the UI before getting into memory saving stuff - which is harder IMO.
const form = document.getElementById("form");
const input = document.getElementById("input");
const todosUL = document.getElementById("todos");
I don't want the form to have default behavior, I want to give it my own lines of behavior:
form.addEventListener("submit", (e) => {
e.preventDefault();
addTodo();
});
function addTodo(todo) {
let todoText = input.value;
if (todo) {
todoText = todo.text;
}
These if are just to make sure that the todo and todoText exist, to make sure the user is typing an input.
if (todoText) {
const todoEl = document.createElement("li");
if (todo && todo.completed) {
todoEl.classList.add("completed");
}
todoEl.innerText = todoText;
todoEl.addEventListener("click", () =>
todoEl.classList.toggle("completed")
);
todoEl.addEventListener("contextmenu", (e) => {
e.preventDefault();
todoEl.remove();
});
todosUL.appendChild(todoEl);
Adding this last input value sets to empty the input box after the user hits enter.
input.value = "";
}
}
This works perfectly now, but it is only stored in the DOM and if the user closes the window, the list goes away. What I need to do is now setup the local storage feature so that the List stays there even after the user closes the window.
To save the To Do's in the local storage. There are browser APIs that I can use for that:
A bit of theory: When you want to save something to local storage, you set the item, you give it a key and a value. Everything you save in local storage will be saved as a string, if you want to save an array you have to do it a bit differently (stringify) just as follows:
For strings:
localStorage.setItem('name', 'Eric')
For arrays or objects:
localStorage.setItem('name', JSON.stringify(obj) - To save an array as a string
JSON.parse(localStorage.getItem(obj)) - To get said string and bring is as an array
Anyway, enough theory, I'll bring back the whole JavaScript code to modify it directly and you understand (in case anyone is reading this).
const form = document.getElementById("form");
const input = document.getElementById("input");
const todosUL = document.getElementById("todos");
//This will be stored as a string, so we need to parse it back to an array when it is fetched
const todos = JSON.parse(localStorage.getItem("todos"));
//Then we tell the local storage "if there are ToDos, loop through them and bring them to me"
if (todos) {
todos.forEach((todo) => addTodo(todo));
}
form.addEventListener("submit", (e) => {
e.preventDefault();
addTodo();
});
function addTodo(todo) {
let todoText = input.value;
if (todo) {
todoText = todo.text;
}
if (todoText) {
const todoEl = document.createElement("li");
if (todo && todo.completed) {
todoEl.classList.add("completed");
}
todoEl.innerText = todoText;
todoEl.addEventListener("click", () => {
todoEl.classList.toggle("completed");
// I add here the update so when the user opens the window again, the item is toggled completed if it originally was.
UpdateLocalStorage();
});
todoEl.addEventListener("contextmenu", (e) => {
e.preventDefault();
todoEl.remove();
// I add here the updatefor the same reasons as above.
UpdateLocalStorage();
});
todosUL.appendChild(todoEl);
input.value = "";
// Then it is pretty easy, I set up a function that saves the items into the local storage
UpdateLocalStorage();
}
}
function UpdateLocalStorage() {
todosEl = document.querySelectorAll("li");
// This takes all the list items and saves them into a variable as a node list, which behaves like an array.
const todos = [];
todosEl.forEach((todoEl) => {
todos.push({
text: todoEl.innerText,
completed: todoEl.classList.contains("completed"),
});
});
localStorage.setItem("todos", JSON.stringify(todos));
}
These projects are based on a CSS, HTML and JS paid course I got on Udemy by Brad Traversy. I made my own changes and tweaks but the template so to speak, is his idea and I do not claim them to be my own. If you are looking to practice CSS and JavaScript, his courses are the way to go, check them out on Udemy.