Story-time: Tackling No Double Favorite Cat
Tags: javascript, html
Published: 2022-04-13 02:01:20

I wanna talk about a feature I struggled with implementing on my recent project to use a public API to create a single page application.

So the idea for my project was to create an application to help someone pick their ideal cat to "adopt". The user could search from a list of cat breeds available on the https://thecatapi.com/, and the search would return a few bullet points of information. If the user likes a cat, they can "favorite" that breed, and the image and name will be stored in the favorites div while the user searches more cat breeds.

Before diving into the coding, I thought the difficult part of this project would have been the DOM manipulation combined with the asynchronous behavior of the GET request. But surprisingly... I found myself stuck trying to perfect the "favoriting" feature for a good chunk of time. 

So this is a section of what the basic application looks like after searching a cat breed:

![SPAScreenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vfi77decdlofpedwdm0v.png)

If the favorite button is clicked, a small image with the breed name underneath would appear in the empty favorites div. The only constraints I wanted were:

- Maximum 3 favorites
- Can't favorite one breed twice

Ok... easy enough. First I tackled doing maximum 3 favorites. This would be called within a function handling the "favorite!" click event listener. newFavorite is an html div element with the cats image and breed name inside:

```
function checkFavorites(newFavorite) {
    const favorites = document.getElementById('favorites')

    if (favorites.children.length === 3) {
        favorites.removeChild(favorites.children[0])
        favorites.appendChild(newFavorite)
    }
    else {
        favorites.appendChild(newFavorite)
    }
```

I test ran it, and it worked fine. Simple, great! Ok-- next part.

The first problem I had: how was I going to check if the newFavorite cat breed I was passing into my function was already favorited? The favorites are all added into the "favorites" div using appendChild(newFavorite). This is the HTML after one cat breed is added to favorites: 

![HTML Snippet](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ruveoqwu3sh26hn7hsh1.png)

So I could check all the children of the "favorites" div element. Probably easiest to iterate through favorites.children and check if the breed name in newFavorites matches a child of favorites. If I was to code this by itself, without the 3 cat limit, this is what I came up with:

```
function checkFavorites(newFavorite) {
    const favorites = document.getElementById('favorites')
    const currentBreedName = newFavorite.getElementsByTagName('p')[0].textContent

    let checker = true
    for (i=0;i < favorites.children.length ;i++) {
        if (favorites.children[i].getElementsByTagName('p')[0].textContent === currentBreedName) {
            checker = false
        }
    }

    if (checker === true) {
        favorites.appendChild(newFavorite)
    }
    else {
        alert('AREADY IN FAVORITES')
    }
```
The for loop iterates through all the current children of favorites and uses an if statement to check if the name matches the one I am trying to add. I can't use appendChild within my for loop, or else I could append more than once. So I create a 'checker' variable and use a second if statement to action the appendChild after the for loop is done.

Took me a little bit to figure that one out, but sure enough, this code works by itself. Next step would be to combine it with the code I made to limit to 3 favorites. If combined in the right order, this should work right?

I'm not exactly sure why I decided to cap it at 3 cats or even at all. Maybe to help the user with decision paralysis or make it aesthetically pleasing or... Anyways...

If I copy paste my first code block and put it right after my second code block... it doesn't work. If you favorite the same breed twice, it will alert you it is "ALREADY IN FAVORITES", but when it passes the second code block, since it hasn't reached 3 children, it appends the double-favorited cat.

And vice versa: if I order the two code blocks the other way, I get the same behavior. So still not working. Maybe if I put code block 1 in the middle of code block 2? Code block 1 inside the for loop of code block 2? A nested if statement within a for loop within an if statement???

I wasn't expecting this to stump me but I was little low on morale at the time. Maybe I should change my constraints to make the coding easier?

Somehow-- it eventually came to me. If I combined the two if statements using &&, I could get the elements appended/removed in the right order to get the behavior I was looking for. And much easier to read than some of the other options I was thinking of trying.

This is the final version of the function:

```
function checkFavorites(newFavorite) {
    let favorites = document.getElementById('favorites')
    let currentBreedName = newFavorite.getElementsByTagName('p')[0].textContent

    let checker = true
    for (i=0; i < favorites.children.length ;i++) {
        if (favorites.children[i].getElementsByTagName('p')[0].textContent === currentBreedName) {
            checker = false
        }
    }
    if (favorites.children.length != 3 && checker === true) {
        favorites.appendChild(newFavorite)
    }
    else if (favorites.children.length === 3 && checker === true) {
        favorites.removeChild(favs.children[0])
        favorites.appendChild(newFavorite)
    }
    else {
        alert('AREADY IN FAVORITES')
    }
}
```
Combining (favorites.children.length != 3) boolean with (checker === true) allows me to appendChild only once, and making sure I am following both my constraints at once. Alternatively, I could have done the same thing with else if statements, but I think it would have affected the readability.

Simple enough-- I thought. Anyone have a better solution?