Yesssss, I made it! Not exactly 50 projects for 50 days, but almost. Doing a small or medium project a day has not only improved my DOM skills massively, but it also taught me that discipline is the only way forward, as you won't always be motivated to code, especially when days start to be a bit brighter and warmer. But let's jump into my last projects.
41st Project: Validate Account
in this project we used Webkit, something that I had always putting off learning, probably just because I didn't fancy writing too many ---'s 😂, as at work we already use BEM (block, element, modifier) methodologies. Researching it, I discovered that is is a simple HTML and CSS web browser rendering engine (used by popular browsers such as Chrome and Safari). Also, from what I understood from a Stack Overflow answer, it is used as a prefix on CSS selectors for properties that you only want to use on a certain engine....and many hope that this specification goes away eventually.
.code::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
The actual JavaScript code wasn't super hard, though I hard a rather hard time understanding why the key I needed was keydown rather than keyup. Yes, I still have some issues with some event listeners when it comes to keys events, as so much stuff has also now been deprecated.
codes.forEach((code, index) => {
code.addEventListener('keydown', (e) => {
if(e.key >= 0 && e.key <= 9) {
codes[index].value = '';
setTimeout(() => codes[index + 1].focus(), 10)
} else if (e.key === 'Backspace') {
setTimeout(() => codes[index - 1].focus(), 10)
}
});
});
42nd Project: Live User Filter
This project was similar to the GitHub profiles one, though in this case we query the Randomuser API instead. The aim is to have a text input where the user types either a name or a location, and then the code will filter by the inputted words, and show a picture with the related info.
async function getData() {
const res = await fetch('https://randomuser.me/api?results=50')
const { results } = await res.json()
result.innerHTML = ''
results.forEach(user => {
const li = document.createElement('li');
listItems.push(li);
li.innerHTML = `
<img src="${user.picture.large}" alt="${user.name.first}">
<div class="user-info">
<h4>${user.name.first} ${user.name.last}</h4>
<p>${user.location.city}, ${user.location.country}</p>
</div>
`
result.appendChild(li);
});
};
43rd Project: Feedback UI Design
Well, this was a change! I learnt about Event Bubbling in this project, as a way to avoid another forEach loop. Event bubbling is a funny name to refer to the actual bubbling up of events from the DOM tree up. It is very interesting and also very functional, as it saves up a lot of code and repetition. Essentially, instead of targeting each child element of a parent node/element, you attach an eventListener to the parentNode or parentElement, so that it'll act onto how many children you set it to.
panel.addEventListener('click', (e) => {
if (e.target.parentNode.classList.contains('rating')) { //If the parent node contains rating class
removeActive();
e.target.parentNode.classList.add('active');
defaultRating = e.target.nextElementSibling.innerHTML
};
});
44th Project: Custom Range Slider
This project was harder than I expected, with a lot of 'edge' cases to account for, such as using -webkit to cater for different browsers, and a couple new methods I wasn't aware of.
The goal of the project is to move the label of a progress bar toward whichever direction I am dragging the circle towards. The two new methods I have learnt here are: the window's getComputedStyle and the CSS method getPropertyValue. The first returns all of the CSS properties applied to the element I target, and the second gets me whichever property I want from it.
const range = document.getElementById('range');
range.addEventListener('input', (e) => {
const value = +e.target.value;
const label = e.target.nextElementSibling;
const rangeWidth = getComputedStyle(e.target).getPropertyValue('width');
const labelWidth = getComputedStyle(label).getPropertyValue('width');
const numRangeWidth = +rangeWidth.substring(0, rangeWidth.length - 2);
const numLabelWidth = +rangeWidth.substring(0, labelWidth.length - 2);
const max = e.target.max;
const min = e.target.min;
const left = value * (numRangeWidth / max) - numLabelWidth / 2;
label.style.left = `${left}px`
label.innerHTML = value;
});
45th Project: Netflix Navigation
This mobile menu navigation reminded me of the 14th project, which was roughly the same, just with a bit less JavaScript code and a rotating effect. The aim of this project was instead to replicate Netflix's mobile menu using the burger menu icon.
As I said previously, l have now understood the general concepts used to do the main effects, so I am focusing here on the smaller details and properties such as text-transform, which is the CSS version of the JavaScript method .toUpperCase(). I also played around with transition-delay and text-decoration.
.nav-black {
background-color: rgb(34, 31, 31);
width: 60%;
max-width: 480px;
min-width: 320px;
transition-delay: 0.3s;
}
46th Project: Quiz App
This was a fun and rather fun one. The data is in an array ready to be queried, so it makes it easier for the data to be grabbed. I got to practice my ever-lasting issues with indexes and iteration, which was good, though I think that what makes it hard for me to distinguish between indexes and variables, is that indexes (which in my mind are integers) sometimes are called with string names.
submitBtn.addEventListener('click', () => {
const answer = getSelected();
if (answer) {
if (answer === quizData[currentQuiz].correct) {
score++;
}
currentQuiz++;
if (currentQuiz < quizData.length) {
loadQuiz()
} else {
quiz.innerHTML = `<h2>You answered correctly at ${score} / ${quizData.length} questions</h2>`
}
}
})
For example, on line 4, I am checking whether the answer which I previously obtained by checking its ID, correspond to the correct one for whichever page of the quiz I am looking at. In this case, currentQuiz is the index which I use to identify which page of the quiz I am on. The index then gets increased on line 82, to move to the next quiz.
47th Project: Testimonial Box
Cute little project to display testimonials in boxes, and with a progress bar to allow the user to see how long it takes to disappear and move to the next profile. The only new thing here was the animation of the progress bar, which we animated via CSS using a linear infinite animation of 8 seconds, which we defined in @keyframes, so that it would effectively grow from 0% to 100% in on the X (horizontal) axis, starting from the left side (if left without transform-origin, it would start from the middle of the bar and grow towards both directions).
.progress-bar {
background-color: white;
height: 4px;
width: 100%;
animation: grow 8s linear infinite;
transform-origin: left;
}
@keyframes grow {
0% {
transform: scaleX(0);
}
}
@media(max-width: 768px) {
.fa-quote {
display: none;
}
.testimonial-container {
padding: 20px 30px;
}
}
48th Project: Random Image Feed
A very short and straight to the point project, not much different from what I have been doing already in the other projects.
49th Project: Todo List
Aaaaah and we eventually got there. The prettier spin-off of the notes app, but I like styling DOM elements so it was rather fulfilling in the end. The only thing that changes from a normal todo list is the fact that I am making use of contextmenu to perform the deletion operation. This simply means that I only had to right click on one item so that it disappeared from the DOM, rather handy!! On the other hand, by using the usual click event, I draw a line through, as I apply the following css: text-decoration: line-through.
.todos li.completed {
color: grey;
text-decoration: line-through;
}
I also like that we worked a bit on the localStorage, by setting and getting the key-value pairs to and from it. After each event has ended, we update the localStorage so that it reflects the changes that we have made.
function updateLocalStorage() {
todosElement = document.querySelectorAll('li');
const todos = [];
todosElement.forEach(todoElement => {
todos.push({
text: todoElement.innerText,
completed: todoElement.classList.contains('completed')
});
});
localStorage.setItem('todos', JSON.stringify(todos));
};
function addTodo(todo) {
let todoText = input.value;
if (todo) {
todoText = todo.text
};
if (todoText) {
const todoElement = document.createElement('li');
if (todo && todo.completed) {
todoElement.classList.add('completed')
}
todoElement.innerText = todoText;
todoElement.addEventListener('click', () => {
todoElement.classList.toggle('completed');
updateLocalStorage();
});
todoElement.addEventListener('contextmenu', (e) => {
e.preventDefault();
todoElement.remove();
updateLocalStorage();
});
todosUL.appendChild(todoElement);
input.value = '';
updateLocalStorage();
};
};
50th Project: Insect Catch Game
Yes we made it! It wasn't exactly 50 days, but almost! This last project was one of the longest ones, with a lot of DOM functionality and manipulations which I found fun, but that I think I might done with now... not sure if you noticed but toward the end of the projects, stuff got a tad repetitive, though still very useful and engaging.
All in all it was a very good exercise, I learnt a lot and I am sure that after over 15/20 hours spent on this set of projects, I will not forget anything 😊
Top comments (8)
Congratulations on completing the challenge! Glad to see you used the triple backticks rather than screenshots in this article!
So what is next on the challenge list?
If you want an unsolicited suggestion, creating a writing plan and write, I have been enjoying putting a writing plan together and putting out some silly posts and I think it really helps you learn faster!
Anyway, congratulations once again, from sunny 😲 Preston!
Thank you so much 🤩
Yes, getting there with all the back ticks, next time I'll use JSFiddle.
I am on annual leave at the moment as I change jobs, so I have been working with Next.js and will start GraphQL really soon, sooooo I suppose it will be a JavaScript kinda of party month 👌🏻
Thank you, I will check it out.
Greetings from sunny (for real), Italy 🇮🇹
Where is the challenge list please?
Hey! I have followed this one 😊
udemy.com/course/50-projects-50-days/
Congratz 👌 it as a really good challenge. Well done 👍
Thank you, it was very much down to discipline...🥳
Here is my last challenge if you want to discover : dev.to/geminii/challenge-2-crappo-...
Maybe you could find inspiration for your next challenge :)
Impressive list of projects!
BTW, I also have an Italian cousin named Chiara ;))