#JavaScriptmas 2023 Day 19 to 24
Tags: webdev, scrimba, javascript, javascriptmas
Published: 2023-12-24 10:28:24

Welcome back! If you've been following along, we've already solved challenges of Days 1 to 18 in our journey. Now, buckle up as we dive into the last leg of this adventure of Days 19 to 24. 

## Day 19: Debug Jingle Words
### Task

- There are loads of problems in JS. Find them, and fix them!

```
function handleGuess(e) {
    e.preventDefault()
    let currentState = []
    let input = document.getElementById('user-input')
    let guess = input.value
    const guessArr = guess.split('')
    for (let i = 0; i < wordArr.length; i++) {
        if (guessArr[i] === wordArr[i]) {
            currentState.push(guessArr[i])
        } else {
            currentState.push("-")
        }
    }
    renderGuess(currentState)
    checkWin(guess)
    input.value = ''
}
```

The problems causing the errors were:

- The `id` of the input was incorrect. 
- Instead of using `guess.split(' ')`, it was necessary to use `guess.split('')` to split the string into individual letters.
- Implemented a `for` loop to iterate through each letter of both the `guess` and `wordArr`.
- Finally, when a letter is matched, it is pushed; otherwise, ('-') is pushed.

```
function renderGuess(arr) {
    const wordHtml = arr.map((letter) => {
        let html = ""
        if (letter === "-") {
            html += `<span class="letter wrong">-</span>`
        } else {
            html += `<span class="letter correct">${letter}</span>`
        }
        return html
    })
    wordDisplay.innerHTML = wordHtml.join('')
}
```
![Visual clue indicating whether entered letter is correct](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7azh8k6e736rg9nf39br.png)

I have also updated the `renderGuess` function to provide users with a visual clue indicating whether the letter is correct or not.

Check my scrim [here](https://scrimba.com/scrim/co70545a58b1263c02719f922).

## Day 20: Save Santa!
### Task

- Save Santa by removing the lions, tigers, bears, and other nefarious creatures from the deeply-nested array. Following is the input.

```
const dangerArray = [
    ["🎅", "👺"],
    [
        ["🎅", "🦁"],
        ["👹", "🎅"]
    ],
    [
        [
            ["🎅", "🐻"],
            ["🧌", "🎅"]
        ],
        [
            ["🐯", "🎅"],
            ["🎅", "😈"]
        ]
    ]
];
```

```
function saveSanta(arr) {
    for(let i=0; i<arr.length; i++ ){
        if(Array.isArray(arr[i])){
            saveSanta(arr[i])
        }
        else{
            if(arr[i]!=="🎅"){
                arr.splice(i, 1)
            }
        }
    }
    return arr
}

console.log(saveSanta(dangerArray))
```

- First we iterate over the array and check if each item is an array. 
- If it is, a recursive call to the current function is made.
- Otherwise, we check if the item is not Santa. 
- If it isn't, the item is sliced from the array.

Check my scrim [here](https://scrimba.com/scrim/co9c247d5b0d9dfac4d45c761).

## Day 21: Expanding Search Bar
### Task

- ﻿Search input: Takes upto 1/3 of the width of its container

- When the user clicks into the search bar:
    - Input grows to entire width of its parent container with smooth transition
    - Shrinks back to original size when user clicks away
    - Blue border
    - Bonus: placeholder text is not visible when user clicks inside the search bar

```
<div class="container">
  <label for="search-field" class="visually-hidden">Search</label>
  <input type="search" 
    class="search-bar" 
    id="search-field"
    placeholder="Search..."/>
</div>
```

```
html, body {
    margin: 0;
    padding: 0;
}

:root {
    --search-border: #bbb;
    --search-focus-border: #3a71ca;
}

.visually-hidden {
	position: absolute !important;
	width: 1px !important;
	height: 1px !important;
	padding: 0 !important;
	margin: -1px !important;
	overflow: hidden !important;
	clip: rect(0,0,0,0) !important;
	white-space: nowrap !important;
	border: 0 !important;
}

.container{
    width: 80%;
    margin: 30px auto 0;
}

.search-bar{
    border: 1px solid var(--search-border);
    border-radius: 4px;
    padding: 8px;
    width: 30%;
    transition: width .2s linear;
}

.search-bar:focus{
    width: 100%;
    border-color: var(--search-focus-border);
}

.search-bar:focus::-webkit-input-placeholder {
    opacity: 0;
}

.search-bar:focus::-moz-placeholder {
   opacity: 0;
}
 
.search-bar:focus:-ms-input-placeholder {
    opacity: 0;
}
 
.search-bar:focus:-moz-placeholder {
   opacity: 0;
}
```
The trick here is that we initially set the width of `.search-bar` to 30% of its parent container. When clicked, we utilize the [`:focus`](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus) selector to dynamically adjust the width to 100%, employing a transition effect for a smooth visual transition. To hide the placeholder text, browser-specific placeholder selectors are used, and a style of opacity: 0; is applied.

Check my scrim [here](https://scrimba.com/scrim/co81d445fbf3ef31a77f0abb9).

## Day 22: Gift App
### Task

- Make it so that the data doesn't disappear on reload.
Use localStorage.

```
let people = JSON.parse(localStorage.getItem("people")) || []

if(people) renderList(people)

addButtonEl.addEventListener("click", function() {
    let inputValue = inputFieldEl.value
    
    if (inputValue) {
        people.push(inputValue)
        localStorage.setItem("people", JSON.stringify(people))
        clearInputFieldEl()
        
        renderList(people)
    }
})

function appendPersonToPeopleListEl(person) {
    
    let newEl = document.createElement("li")
    
    newEl.textContent = person
    
    newEl.addEventListener("dblclick", function() {
        let index = people.indexOf(person)

        people.splice(index, 1)
        localStorage.setItem("people", JSON.stringify(people))
        img.src = "./images/gifts.webp"
        renderList(people)
    })
    
    peopleListEl.append(newEl)
}
```
- When a new person is added to our Gift App list, we use `localStorage.setItem("people", JSON.stringify(people))` to store the people array with the key named "people" in [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage). The `stringify` function is used to convert a JavaScript object (in this case, the people array) into a JSON-formatted string.
- The object is similarly updated when a person is removed from the list. 
- Lastly, to retrieve the data, we use `JSON.parse(localStorage.getItem("people"))`. The `parse` function is used to convert a JSON-formatted string back into a JavaScript object.

Check my scrim [here](https://scrimba.com/scrim/co4ea45a2b8f5546ebc12717f).

## Day 23: Toggle Switch
### Task

- On click, toggle switch moves from one side to another
- Cursor becomes pointer
- Match styles
- No JavaScript

```
<div class="toggle-wrap">
  <label for="toggle">
    <input type="checkbox" id="toggle" class="toggle-input"></input>
    <div class="toggle-switch"></div>
  </label>
</div>
```

```
#toggle{
    display: none;
}

.toggle-wrap{
    width: 80px;
    height: 40px;
    border: 2px solid var(--toggle-border);
    border-radius: 100px;
    background: var(--toggle-bg);
    display: flex;
    align-items: center;
}

label{
    width: 100%;
    cursor: pointer;
}

.toggle-switch{
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: var(--toggle-switch-bg);
    transition: transform 0.2s linear;
    backface-visibility: hidden;
}

.toggle-input:checked + .toggle-switch{
    transform: translateX(45px);
}

.toggle-input:not(:checked) + .toggle-switch{
    transform: translateX(5px); 
}
```
- Here, we hide the actual checkbox, but since we use the `id` and `for` attributes in both the label and input, we can determine whether it is checked or not. 
- We utilize the [`:checked`](https://developer.mozilla.org/en-US/docs/Web/CSS/:checked) selector and a combination selector to shift the slide to the right side. 
- To check if it is unchecked, we use [`:not()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:not) selector in combination with `:checked` and translate the position to its initial state.
- The transition property is used to ensure a smooth animation. 

Check my scrim [here](https://scrimba.com/scrim/coe01435a9119cabdcf26708d).

## Day 24: Christmas Lights
### Task

- Make Christmas tree lights flash on and off every 800 milliseconds on a permanent loop.
- Make the blue and red lights toggle on and off alternatively. So first the red lights come on, and then as they go off, the blue lights come on.

```
const blues = document.querySelectorAll('.blue');
const reds = document.querySelectorAll('.red');
let isRed = true;

function toggleLights() {
    if (isRed) {
        reds.forEach(redLight => {
            redLight.classList.add("lights-on");
        });
        blues.forEach(blueLight => {
            blueLight.classList.remove("lights-on");
        });
    } else {
        blues.forEach(blueLight => {
            blueLight.classList.add("lights-on");
        });
        reds.forEach(redLight => {
            redLight.classList.remove("lights-on");
        });
    }
    isRed = !isRed;
}

setInterval(toggleLights, 800);
```

- We use `setInterval(toggleLights, 800);` to call the `toggleLights` function every 800 milliseconds. 
- In the function, we check if `isRed` is true. If it is, we _add the `lights-on` class to the red lights_ and _remove it from the blue lights_. 
- Otherwise, we _add the `lights-on` class to the blue lights_ and _remove it from the red lights_.

Check my scrim [here](https://scrimba.com/scrim/co04e4759b8878fe0a1eb5c1a).

As we finish up the 24-day coding adventure of #JavaScriptmas, I can't help but smile thinking about how much fun it brought to my daily routine. Solving a new challenge each day became something I looked forward to, keeping me consistent and excited.

I want to say a big thank you to everyone who followed along and joined me in this coding journey. Your support and enthusiasm made it even better!

Thanks for being a part of it. Until next time, happy coding!