Studio Ghibli Watch List

What can it do?

I made a web application using the Studio Ghibli API. It is a culmination of everything I have learned up to this point in time. I made it with the intended purpose of a watch list, so that the user could mark the Studio Ghibli films that they have watched. The list of films are on the left side, and a more detailed view of a single film on the right. The user can click on a film from the list on the left and have it show on the right with more details, as well as highlight the film on the list that was clicked with a selected view. Both the list and the detailed view have checkbox buttons that correlate to the watched value, so if the user clicks the button the watched value of that specific film with change, from unwatched to watched or vise versa. This action is synced to the film itself, so it doesn't matter if the click has been made on the list or the detail, it will be the same. There is also a filter above the list where the user can filter the list view by whether the film has been marked as watched, as well as which ones were not marked as watched. There are also filters for each of the directors, and for all of the films with no filter active. On a side note, these filters do not stack. The currently active filter will show inside of the filter bar, so the user can always see and know which filter is in use.

How does it do this?

Almost all of the functionality of this web application lies in the JavaScript, the rest is CSS. The parts from the CSS that I like best are the button hovering that inverts the button color and changes the cursor, and the combination of the media queries and displays to have the contents adjust to the screen size.

The initial functionality of this web application are in the fetch requests to the API, which I placed inside of there own functions and called another function with the fetched data to append that data to the DOM. I have two of these fetch requests one for the list and one using an id for the detail, both with they're respective rendering functions. The fetch request functions and rendering functions work in tandem to have the information show on the DOM. I have an extra function which utilizes a while loop to remove the detail's information from the DOM to make room for new information, for when a different film is selected and adds that new information. It acts as a reset for the detailed view.

I have another fetch request within both of the rendering functions, which is to the JSON file. It checks the value of watched from the stored data, and changes the checkbox accordingly. This way the checkbox appears either checked or unchecked based on the stored data upon the initial appending to the DOM. The only other fetch request is another to the JSON file, which this request is in a function that is called when a click event is triggered. It changes the watched value and updates it on the stored data of the JSON file. The click event triggered is a little complicated. The event listener is on the checkbox button and calls the function dubbed handleCheck. The first thing that this function does is it identifies the checkbox buttons of the list on the left and the detail on the right. Since there are multiple checkboxes on the list, it turns them into an iterable array. Then if the currentTarget of the click event is the checkbox button or contains the class of "checkbox" it will call the function with the fetch request to update the JSON file. It then iterates through the array of checkboxes from the list and if the id of the checkboxes match then it change the CSS to make the button appear checked or unchecked. Each of the checkboxes have the id of the film from the API attached as the id, and iterating through the array checks the ids that are already on the DOM so the CSS can change what is already appended to the DOM. The rest of the handleCheck function checks if the id of the list's checkbox matches the detail's checkbox, and syncs the change in CSS if they do. I also ended up adding the "contains class" if statement to all of my event handlers to clarify the intended target of the click event, so that the functionality would only be applied where its intended in a more fool-proof way. All of this turned out to look like this:

function handleCheck(e) {
  const detailCheck = document.querySelector("#detail-container").querySelector(".checkbox")
  const cardChecks = document.querySelector("#cards-container").querySelectorAll(".checkbox")
  const cardChecksArray = [...cardChecks]
  const detailCheckedBtn = detailCheck.querySelector("button")

  if(e.currentTarget.classList.contains("checkbox")) { // checks if click target is checkbox
    updateWatched(e.currentTarget.id)
    cardChecksArray.forEach(cardCheck => {
      if(e.currentTarget.id === cardCheck.id) { // matches click target and list to check of correct checkbox
        if(checked === true) {
          e.currentTarget.querySelector("button").id = "checked"
        }
        else {
          e.currentTarget.querySelector("button").id = ""
        }
        if(cardCheck.id === detailCheck.id) { // syncs check of card and detail
          if(checked === true) {
            cardCheck.querySelector("button").id = "checked"
            detailCheckedBtn.id = "checked"
          }
          else {
            cardCheck.querySelector("button").id = ""
            detailCheckedBtn.id = ""
          }
        }
      }
    })
  }
}

Just like the handleCheck function the handleFilter function is also a little bit complicated because quite a few things go on inside. Although instead of nested if statements like the handleCheck function, the handleFilter function has five else ifs, three of which have a nested if statement. Each of the else ifs act as separate functionality based on what is the desired filter.

The first thing this function does is make an array of from the list just like in the handleCheck function. Then it acquires the id of the click target to make sure it is the filter being clicked on, and if it is it displays the dropdown menu. Then on further clicks the function checks the inner text of the clicked target and displays or hides the content based on that text. Which it also identifies the content to display or hide by iterating through all of the cards on the DOM. It also displays the targeted filter within the filter bar, so than what is currently being filtered is visible. All this is excluding “Director” since it simply a label and not a button, and has no functionality other than hovering over in order to display the dropdown menu. All of the functionality within handleFilter are click events to show and hide items.

I had initially wanted to use select and option tags for the filter, but since I wanted a nested dropdown of directors I could not get the result I wanted. I then switched it to be a ul, where I could not only have the functionality I wanted but also more easily customize its appearance. Since the functionality lies in the JavaScript, regardless of what tags are used the functionality remains the same, only the application of it an its appearance differ.

The only other click event for this web application is the one on the list of films itself, which displays the same film in the more detailed view on the right side. Outside of this function, which is only called upon a click event, is another function that runs to match the detail and card, and makes the card appear selected if they are the same. This function is called inside the rendering detail function to more easily acquire the information from the detail as well as reacquiring it once the detail is refreshed.

Improvements...

There are a few things I think could be improved upon, but cannot with my current knowledge and abilities. The first being that the selected appearance of the list's film doesn't always show upon the initial load-up, but it works fine with the click event. The other things I disliked where that I had fetch requests inside the rendering functions, rather than in their own separate functions, but I couldn't get it to work the way I wanted otherwise. And for some reason my query selectors wouldn't work in the global scope. I also wish that I could improve its load speed and efficiency, but I currently don't know how to do that while keeping the same functionality.

30