#JavaScriptmas 2023 Day 13 to 18
Tags: webdev, scrimba, javascript, javascriptmas
Published: 2023-12-19 14:08:42

Welcome back! If you've been following along, we've already solved challenges of Days 1 to 12 in our journey. Now, buckle up as we dive into the next leg of this adventure Days 13 to 18. Let's start.

## Day 13: Christmas Dinner Picker
### Task

- Write the code to help a user choose the perfect Christmas dinner idea based on the number of people attending.
- Include a checkbox for the user to specify the meal should be vegetarian-friendly.
- Dinner suggestions (or choose your own!):
Vegetarian: Winter Squash Risotto
4 people or less: Ham
5+ people: Turkey

```
const numOfGuests = document.getElementById('guests')
const btnCalculate = document.getElementById('btn')
const isVegetarian = document.getElementById('vegetarian-input')

const dinnerOption = document.getElementById('food')

btnCalculate.addEventListener('click', function(){
    const guests = +numOfGuests.value
    if(isVegetarian.checked){
        dinnerOption.textContent = 'Winter Squash Risotto'
    } else{
        dinnerOption.textContent = guests <= 4 ? 'Ham' : 'Turkey'
    }
    numOfGuests.value = ''
})
```

- When the 'Calculate' button is first pressed, we retrieve the number of guests. Adding `+` is a trick to convert string to a number.
- If the 'Vegetarian?' checkbox is checked, the dinner option appended to the UI is 'Winter Squash Risotto'. 
- Otherwise, we check if the number of guests is less than or equal to 4.
- If it is, 'Ham' is chosen; otherwise, 'Turkey' is chosen and then appended to the UI.

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

## Day 14: Lonely elf
### Task

- Write a function to duplicate the elf when the button is clicked.
- Limit the number of elves to 6 per row.
- Make sure that the elves stay in the elf hangout zone, no matter how many there are.
- Limit the total number of elves to 100.

```
let elfCount = 2

btn.addEventListener("click", duplicateElf)

function duplicateElf(){
    if(elfCount === 2) elfHeader.innerText = 'Elves'
    if(elfCount <= 100){
        elf.textContent += '🧝'
        elfCount += 1
    }else{
        const enoughElves = document.getElementById('more-elfs')
        enoughElves.style.display = "block"
    }
}
```

- When the 'Duplicate elf' button is pressed, we first change the title to 'Elves'.
- Next, we check if `elfCount` is less than or equal to 100.
- If it is, we append one more elf to the UI and increase the count of `elfCount`.
- Otherwise, we append 'No more elves can be added' to UI.

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


## Day 15: Archery Target
### Task
- Create CSS only archery target

#### HTML

```
<div class="rings">
 <div class="ring ring-1">
  <div class="ring ring-2">
   <div class="ring ring-3">
    <div class="ring bullseye"></div>
   </div>
  </div>
 </div>
</div>
```

#### CSS

```
.rings{
    position: relative;
}

.ring{
    border-radius: 50%;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%)
}

.ring-1{
    width: 300px;
    height: 300px;
    background: var(--black);
}

.ring-2{
    width: 220px;
    height: 220px;
    background: var(--blue);
}

.ring-3{
   width: 140px;
   height: 140px;
   background: var(--red); 
}

.bullseye{
   width: 60px;
   height: 60px;
   background: var(--yellow); 
}
```

- The main `.rings div` is set to `position: relative;` so that the other rings can overlay it.

```
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%)
```
- The above CSS is to ensure the circles are always centered.
- Subsequently, for each ring, we decrease the width and height by `80px`, ensuring the circle remains perfectly sized, and apply the background color.

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

## Day 16: Naughty list, nice list
### Task

- Write the JavaScript to sort the people in sorteesArr into the naughty and nice lists, according to whether they have been good or not. Then display the names in the relevant place in the DOM.
- Add the option to add new names to the sorteesArr.

```
btn.addEventListener("click", function(){
    for(let i=0; i<sorteesArr.length; i++){
        sort(sorteesArr[i])
    }
})

addPerson.addEventListener('click', function(){
    const newPerson = {
        name: newName.value,
        hasBeenGood: isGood.checked
    }
    sorteesArr.push(newPerson)
    sort(newPerson)
    newName.value = ''
    isGood.checked = false
})

function sort(person){
    if(person.hasBeenGood){
        nice += `<li>${person.name}</li>`
    }else{
        naughty += `<li>${person.name}</li>`
    }
    niceList.innerHTML = nice
    naughtyList.innerHTML = naughty
}
```
- First, we call the `sort()` function for the length of the `sorteesArr`.
- Within the function, we check if the person 'has been good?'. 
- If yes, we create a string of `<li>` elements and add it to the `niceList`. Otherwise, we add it to the `naughtyList`.
- When 'Add person' is pressed, we create a `newPerson` object and push it to `sorteesArr`, followed by a call to the `sort()` function.

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

## Day 17: Word Carousel
### Task
- Create a CSS only Word Carousel.
- Match styles and display 4 things you love.

#### HTML

```
<div class="container">
 <h1>I love</h1>
<div>
```
#### CSS

```
body{
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
}

h1::after {
  content: "";
  animation: carousel 5s steps(1) infinite;
  background: var(--bg-word);
  color: #fff;
  margin-left: 2px;
  padding: 8px 10px;
  border-radius: 4px;
}

@keyframes carousel {
  0% {
    content: "reading";
  }
  45% {
    content: "Scrimba";
  }
  65% {
    content: "coding";
  }
  85% {
      content: "music";
  }
}
```

- We first center the `<h1>` tag by applying styles to `<body>`.
- For the carousel part, we apply styles to the `::after` selector.

```
animation: carousel 5s steps(1) infinite;
```
- The animation is named 'carousel' with a duration of 5s. 
- The [`steps()`](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function) function is set to 1, which means the animation will move in discrete steps rather than smoothly transitioning.
- The `infinite` value is used to make the animation repeat indefinitely.
- Keyframes are added to specify the content to be displayed at different percentages.

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


## Day 18: AI Alt Text Generator
### Task

- Use AI to generate alt text for the image provided by generateImage().
- Pass the alt text to renderImage() as a parameter. 

```
async function generateAltText(blob, imageUrl) {
    const altText = await hf.imageToText({
        data: blob,
        model: "nlpconnect/vit-gpt2-image-captioning",
    })
    renderImage(imageUrl, altText.generated_text) 
}
```

- I used [huggingface](https://huggingface.co/) for this challenge. You can find the documentation [here](https://huggingface.co/docs/huggingface.js/inference/README).
- `hf.imageToText` is a function from the Hugging Face library for generating text from a provided image.
- The `data` passed to it is in the form of a blob, and the `model` parameter represents the specific model used for image-to-text generation.
- In summary, this code requests a model from Hugging Face to generate alt text for an image based on the provided blob. The resulting alt text is then passed to the `renderImage()` function.

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

Would love to hear your thoughts on this. Can't believe it has been 18 days already. Stay tuned for the last iteration of this series.