This is my solution to the Todo app challenge on Frontend Mentor. I used Frontend Mentor to improve my frontend coding skills by building realistic projects. I thoroughly enjoyed this challenge. It was my first Vanilla Js project in a while and it made me hungry for more.
Users should be able to:
- View the optimal layout for the app depending on their device's screen size
- See hover states for all interactive elements on the page
- Add new todos to the list
- Mark todos as complete
- Delete todos from the list
- Filter by all/active/complete todos
- Clear all completed todos
- Toggle light and dark mode
- Drag and drop to reorder items on the list
- Semantic HTML5 markup
- CSS custom properties
- Flexbox
- CSS Grid
- Vanilla JavaScript
- In replicating the provided design, I learned a few things about background images and layout in CSS. Overlowing elements are still a bit of a mystery to me, but for now, I'm proud that I've created a pretty little custom checkbox with two background images:
.checkbox.checkbox-tick {
background-image: url("./images/icon-check.svg"), linear-gradient(135deg, #55ddff
0%, #c058f3 100%);
background-position: center;
background-repeat: no-repeat;
border: 1px solid;
border-image: linear-gradient(135deg, #55ddff 0%, #c058f3 100%) 1;
}
- I was careful to keep my code DRY and all functions pure, for instance by extracting the counter functionality into a separate updateNumberOfUnchecked function. I still think there's room forr improvement and refactoring is definitely something I want to focus more on in the future.
const updateNumberOfUnchecked = () => {
allTodos = document.querySelectorAll("li");
allChecked = document.querySelectorAll(".checkedItem");
numberOfUnchecked = allTodos.length - allChecked.length;
document.querySelector(
"#numberOfUnchecked"
).innerHTML = `${numberOfUnchecked}`;
};
- I learned to execute media queries with JavaScript instead of CSS. This approach was necessary to add event listeners to a DOM element that is hidden in desktop view. The mobileFilterHandler function calls functions that are also called in the desktop event listeners.
const mediaQuery = window.matchMedia("(max-width: 375px)");
if (mediaQuery.matches) {
document
.querySelector(".filter-group.mobile")
.addEventListener("click", mobileFilterHandler);
}
- I learned to add drag & drop functionality to reorder the list, using the helper function getDragAfterElement to identify the closest next list item. This function certainly required a bit of mind gymnastics on my part, mainly due to the reduce function.
const getDragAfterElement = (container, y) => {
const draggableElements = [
...container.querySelectorAll("li:not(.dragging)"),
];
return draggableElements.reduce(
(closest, child) => {
const box = child.getBoundingClientRect();
const offset = y - box.top - box.height / 2;
if (offset < 0 && offset > closest.offset) {
return { offset: offset, element: child };
} else {
return closest;
}
},
{
offset: Number.NEGATIVE_INFINITY,
}
).element;
};
- Following a (code review by @RocTanweer)[https://www.frontendmentor.io/solutions/to-do-app-FbnfuwNVV#feedback], I've implemented localStorage to save to do list items and theme status in the browser cache. In doing so, I came to realise that it is a lot more efficient and concise to save the user input in a dedicated array which is subsequently referenced by the DOM and the storage.
This project has highlighted to me, that I need to ...
- use CSS naming methodologies to keep my stylesheet neater
- learn more about CSS layout
- learn more JavaScript patterns to refactor my code
I also didn't achieve the gradient border effect on checkbox hover. Who would have thought that gradient borders are so complicated 😩 Definitely something on the still-to-learn-list.
- How To Build Sortable Drag & Drop With Vanilla Javascript - I used this tutorial to learn how to implement the drag & drop functionality.
- Every Layout - this interactive resource is gold dust for creating common CSS layouts - all responsive without media queries 😲