The Code

Click the headers below to hide/show the corresponding code excerpts.

                
                  const API_KEY = "???"
                
              

This section is used to declare global constants and variables. The only global constant needed in this app is a key for the TMDB API. It is used every time the app needs to call the fetch method to access data from the TMDB network.

Of course, it's generally bad practice to hardcode potentially sensitive data, such as some API keys, in a place easily accessible to the user (in more ways than one, which I won't reveal to make it easier for those who don't know).

The reason I did it here is because, at the time I built this app, I hadn't learned the best practices for managing secrets. I've since built other websites following those practices, to protect much more sensitive important data.

But at the time, this was out of the scope of the concepts I was learning with this project - especially for data that's free, easy to obtain and replace, and not tied to any confidential information. For what it's worth, I redacted the actual key in this code page.

                
                  async function displayMovies() {
                    let data = await getMovies();

                    const movieListDiv = document.getElementById('movie-list');
                    movieListDiv.classList.add('d-none');

                    const movieListSpinner = document.getElementById('movieListSpinner');
                    movieListSpinner.classList.remove('d-none');

                    movieListDiv.innerHTML = '';
                    const moviePosterTemplate = document.getElementById('movie-card-template');

                    let movies = data.results;

                    for (let i = 0; i < movies.length; i++) {
                      let movie = movies[i];
                      let movieCard = moviePosterTemplate.content.cloneNode(true);

                      let movieImgElement = movieCard.querySelector('.card-img-top');
                      movieImgElement.src = `https://image.tmdb.org/t/p/w500${movie.poster_path}`;

                      let movieTitleElement = movieCard.querySelector('.card-body > h5');
                      movieTitleElement.textContent = movie.title;

                      let movieParagraphElement = movieCard.querySelector('.card-text');
                      movieParagraphElement.textContent = movie.overview;

                      let movieButton = movieCard.querySelector('.je-button');
                      movieButton.setAttribute('data-movieId', movie.id);
                      movieButton.setAttribute('data-movieRank', i + 1);

                      movieListDiv.appendChild(movieCard);
                    }

                    movieListSpinner.classList.add('d-none');
                    movieListDiv.classList.remove('d-none');
                  }
                
              

This function is used to populate the app page with cards containing basic info for each one of the current 20 most-popular films. It is called when the app page is loaded.

Each card will display the official poster, title, and overview of its respective movie. It will also include a "More Info" button that can be clicked to display a modal containing additional details about that movie.

The info for the movies is obtained from a call to an asynchronous function that fetches data from the TMDB API. The resulting object contains an array of other objects, each one containing data for one of the top 20 movies.

These movie objects are stored in the array in order of their popularity ranking, from most to least. As such, these placements will be used to store each movie's rank as a data-attribute in its "More Info" button. Doing this will allow those ranks to be efficiently retrieved and displayed in the movie details modal, since an API call for an individual movie's details does not provide the rank.

In addition to the above-mentioned info on the card, the movie objects also include the movies' IDs on the TMDB website. These IDs will also be stored as data-attributes in the movies' respective "More Info" buttons. This will allow those IDs to be used to pull more-detailed info about each movie when their "More Info" buttons are clicked.

                
                  async function getMovies() {
                    try {
                      let response = await fetch('https://api.themoviedb.org/3/movie/popular', {
                        headers: {
                          'Authorization': `Bearer ${API_KEY}`
                        }
                      });

                      let data = await response.json();
                      return data;
                    } catch (error) {
                      Swal.fire({
                        backdrop: false,
                        title: 'Oops!',
                        text: 'Something went wrong reaching the TMDB API.',
                        icon: 'error'
                      });
                    }
                  }
                
              

This function is used to obtain basic information about the current 20 most-popular films. It is called by the function that gets fired when the app page is loaded.

The movie details are obtained via the fetch method, which calls the TMDB API to asynchronously pull this data from their network. Awaiting the resulting promise yields a Response object, whose json method asynchronously parses this object's data as JSON.

If this function fails at any point, the try/catch block will fire a SweetAlert message informing of the error. Otherwise, the function creates and returns an object containing data for the current 20 most popular films. More specifically, the object has a property named results, which is an array of objects whose elements each contain the data for a single movie.

                
                  async function showMovieDetails(clickedBtn) {
                    const modalBody = document.getElementById('modalBody');
                    modalBody.classList.add('d-none');

                    const modalSpinner = document.getElementById('modalSpinner');
                    modalSpinner.classList.remove('d-none');

                    // get the ID of the movie that was clicked
                    let movieId = clickedBtn.getAttribute('data-movieId');

                    // get the details of the movie with that ID from TMDB API
                    let movie = await getMovie(movieId);

                    // put those details into my modal
                    modalBody.style.backgroundImage = getStrImgBG(movie);
                    document.getElementById('movieDate').innerText = getYear(movie);
                    document.getElementById('movieCert').innerText = await getCert(movieId);
                    document.getElementById('movieTime').innerText = getHM(movie);
                    document.getElementById('movieScore').innerText = getScoreStr(movie);
                    document.getElementById('movieVotes').innerHTML = getVoteStr(movie);
                    document.getElementById('movieRank').innerText = rankFromBtn(clickedBtn);
                    document.getElementById('movieModalLabel').textContent = movie.title;
                    document.getElementById('movieTitle').textContent = movie.title;
                    displayGenreLabels(movie, 'movieGenres');
                    displayTagline(movie, 'movieTagline');
                    document.getElementById('movieOverview').innerText = movie.overview;
                    document.getElementById('moviePoster').src = getPosterPath(movie);

                    let modalFooter = document.getElementById('movieModal').querySelector('.modal-footer');
                    let btnVisit = modalFooter.querySelector('a');
                    setSiteBtnVisibility(movie, btnVisit);

                    modalSpinner.classList.add('d-none');
                    modalBody.classList.remove('d-none');
                  }
                
              

This function is used to populate the movie details modal with information from the TMDB API for a particular movie. It is called by the function that gets fired when the user clicks the "More Info" button for a movie on the app page.

The clicked button - passed in as the lone argument - provides the TMDB ID of the movie that will be displayed in the modal, via a data-attribute that was set on the button when the page was loaded.

Two async functions retrieve the info - one for the most-recent US certification rating, another for everything else. This info is used to populate the various containers of the modal. As this is happening, a spinner is shown until the data has been fully loaded.

                
                  async function getMovie(movieId) {
                    try {
                      let response = await fetch(`https://api.themoviedb.org/3/movie/${movieId}`, {
                        headers: {
                          'Authorization': `Bearer ${API_KEY}`
                        }
                      });

                      let data = await response.json();
                      return data;
                    } catch (error) {
                      Swal.fire({
                        backdrop: false,
                        title: 'Oops!',
                        text: 'Something went wrong reaching the TMDB API.',
                        icon: 'error'
                      });
                    }
                  }
                
              

This function is used to obtain top level details about a movie whose TMDB ID matches the passed-in string. It is called by the function that gets fired when the user clicks the "More Info" button for any movie on the app page.

The movie details are obtained via the fetch method, which calls the TMDB API to asynchronously pull this data from their network. Awaiting the resulting promise yields a Response object, whose json method asynchronously parses this object's data as JSON.

If all of this succeeds, an object containing the data for the movie is created and returned. Otherwise, the try/catch block will fire a SweetAlert message informing of the error.

                
                  async function getCert(movieId) {
                    let cert = 'MPA Rating Unknown';
                    let data = await getMovieReleaseDates(movieId);

                    if (data) {
                      let results = data.results;

                      if (results) {
                        let releaseDates;

                        for (let i = 0; i < results.length; i++) {
                          if (results[i].iso_3166_1 === 'US') {
                            releaseDates = results[i].release_dates;
                            break;
                          }
                        }

                        if (releaseDates) {
                          let latestDate, releaseDate;

                          for (let i = 0; i < releaseDates.length; i++) {
                            releaseDate = new Date(releaseDates[i].release_date);

                            if (releaseDate && (!latestDate || latestDate < releaseDate)) {
                              cert = releaseDates[i].certification;
                              latestDate = releaseDate;
                            }
                          }
                        }
                      }
                    }

                    return cert;
                  }
                
              

This function is used to find the most-recent certification rating given in the US to a movie whose TMDB ID matches the passed-in string. It is called by the function that gets fired when the user clicks the "More Details" button for a movie on the app page.

If the passed-in string is a valid TMDB movie ID, it will be used to make a call to the TMDB API. The result of this call will be a Response object which, when converted to an object via its json method, will have an array of objects as one of its properties.

Each of these objects correspond to different country via its iso_3166_1 property. If one corresponds to the US, its release_dates property will be another array of objects, each one corresponding to a distinct release date in the US along with the certification rating it received. The function will find the most recent release date and return the rating the movie was given for that release.

If no US release dates were found, the function will return the phrase "MPA Rating Unknown".

                
                  async function getMovieReleaseDates(movieId) {
                    try {
                      let response = await fetch(`https://api.themoviedb.org/3/movie/${movieId}/release_dates`, {
                        headers: {
                          'Authorization': `Bearer ${API_KEY}`
                        }
                      });

                      let data = await response.json();
                      return data;
                    } catch (error) {
                      Swal.fire({
                        backdrop: false,
                        title: 'Oops!',
                        text: 'Something went wrong reaching the TMDB API.',
                        icon: 'error'
                      });
                    }
                  }
                
              

This function is used to obtain the release dates (along with their respective certification ratings) in every country for a movie whose TMDB ID matches the passed-in string. It is called by the function used to find a movie's most-recent US certification rating.

The data is obtained via the fetch method, which calls the TMDB API to asynchronously pull this data from their network. Awaiting the resulting promise yields a Response object, whose json method asynchronously parses this object's data as JSON.

If this function fails at any point, the try/catch block will fire a SweetAlert message informing of the error. Otherwise, the function creates and returns an object containing data for release dates and certification ratings for the movie.

More specifically, the object has a property named results, which is itself an array of objects. Each of these objects has a property representing the country code of one particular country; another property is an array of objects containing data on each of the distinct release dates of this film in this country. These release date objects also contain the associated certification rating given on that release date.

                
                  function getStrImgBG(movie) {
                    let strImgBG = 'linear-gradient(rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.75))';

                    if (movie.backdrop_path) {
                      strImgBG += `, url(https://image.tmdb.org/t/p/w500${movie.backdrop_path})`;
                    }

                    return strImgBG;
                  }
                
              

This function is used to obtain the css value for the background-image property of the details modal for a particular movie. It is called by the function that gets fired when the user clicks the "More Info" button for a movie on the app page.

The file name and extension for a particular film's backdrop image is obtained from a passed-in object which was created from a call to the TMDB API for details on that film.

All of TMDB's movie backdrop files are stored in the directory "https://image.tmdb.org/t/p/w500", so appending the file name and extension onto the end of this yields the url of the backdrop. If this image exists, it's combined with a linear gradient to darken it. Otherwise, the gradient itself becomes the backdrop.

                
                  function displayGenreLabels(movie, containerId) {
                    let containerElement = document.getElementById(containerId);
                    containerElement.innerHTML = '';

                    let genres = movie.genres;
                    if (genres) {
                      for (let i = 0; i < genres.length; i++) {
                        let newSpan = document.createElement('span');
                        newSpan.classList.add('genre')
                        newSpan.innerText = genres[i].name;
                        containerElement.appendChild(newSpan);
                      }
                    }
                  }
                
              

This function is used to display a movie's genres (if it has any) in a container having the passed-in ID. It is called by the function that gets fired when the user clicks the "More Info" button for a movie on the app page.

The genres are obtained from a passed-in object that was created from a call to the TMDB API for details on one particular film.

If the object has genre data for this movie, it's in the form of an array of objects, each one having a string for the name of a genre. These genre names are written inside stylized label elements and inserted into the container.

                
                  function displayTagline(movie, containerId) {
                    let containerElement = document.getElementById(containerId);
                    containerElement.innerText = `"${movie.tagline}"`;

                    if (movie.tagline) {
                      containerElement.classList.remove('d-none');
                      containerElement.classList.add('d-block');
                    }
                    else {
                      containerElement.classList.remove('d-block');
                      containerElement.classList.add('d-none');
                    }
                  }
                
              

This function is used to display a movie's tagline (if it has one) in a container having the passed-in ID. It is called by the function that gets fired when the user clicks the "More Info" button for a movie on the app page.

The tagline is obtained from a passed-in object that was created from a call to the TMDB API for details on one particular film. If the the tagline value is non-empty (i.e. if the movie has a tagline), the container will be displayed. Otherwise it will be hidden.

                
                  function getScoreStr(movie) {
                    let score = Math.round(movie.vote_average * 10) / 10;
                    return score.toFixed(1) + '/10';
                  }
                
              

This function is used to obtain the average vote score for a movie on the TMDB website. It is called by the function that gets fired when the user clicks the "More Info" button for a movie on the app page.

The average vote score is obtained from a passed-in object that was created from a call to the TMDB API for details on one particular film. Some math is used to round it to a single decimal point, and then this value is appended to a string to format the score as a value out of 10.

                
                  function getVoteStr(movie) {
                    let compactFormat = Intl.NumberFormat(
                      'en-US',
                      { notation: "compact", maximumFractionDigits: 1 }
                    );

                    return compactFormat.format(movie.vote_count) + ' vote'
                      + (movie.vote_count != 1 ? 's' : '');
                  }
                
              

This function is used to obtain the number of votes for a movie on the TMDB website, as a string in a stylized format. It is called by the function that gets fired when the user clicks the "More Info" button for a movie on the app page.

The vote count is obtained from a passed-in object that was created from a call to the TMDB API for details on one particular film. This count is then converted to a string in the "compact" number format - e.g. 1.2K for 1,243 - concatenated with the word " vote" or " votes" depending on the amount.

                
                  function rankFromBtn(btn) {
                    return '#' + btn.getAttribute('data-movieRank');
                  }
                
              

This function is used to obtain a data attribute value representing a movie rank from a passed-in button element. It is called by the function that gets fired when the user clicks the "More Info" button for a movie on the app page. When returned, the ranking has a hashtag appended to the front of it for stylistic purposes.

                
                  function getPosterPath(movie) {
                    return `https://image.tmdb.org/t/p/w500${movie.poster_path}`;
                  }
                
              

This function is used to obtain the filepath of a movie's poster within the TMDB website. It is called by the function that gets fired when the user clicks the "More Info" button for a movie on the app page.

The poster's file name and extension are obtained from a passed-in object that was created from a call to the TMDB API for details on one particular film.

All of TMDB's movie poster files are stored in the directory "https://image.tmdb.org/t/p/w500", so appending the file name and extension to the end of this yields the url of the poster.

                
                  function setSiteBtnVisibility(movie, siteBtn) {
                    if (movie.homepage) {
                      siteBtn.classList.remove('invisible');
                      siteBtn.href = movie.homepage;
                    } else {
                      siteBtn.classList.add('invisible');
                      siteBtn.href = '';
                    }
                  }
                
              

This function is used to set the visibility of a passed-in button, depending on whether the passed-in movie data object shows it has an official website. The function is called by the one that gets fired when the user clicks the "More Info" button for a movie on the app page, in order to set the visibility of the "Official Website" button on the details modal for that movie.

The passed-in object is one created from a call to the TMDB API for details on one particular movie. If this object has a non-empty string for the movie's website url, it's used as the href of the button and the button is made visible. Otherwise, the button is made invisible.

                
                  function getYear(movie) {
                    let movieDate = new Date(movie.release_date);
                    return movieDate.getFullYear();
                  }
                
              

This function is used to obtain the year of release of a film. It is called by the function that gets fired when the user clicks "More Info" button for a movie on the app page.

The release date is obtained from a passed-in object that was created from a call to the TMDB API for details on one particular film. This date comes in the form of a string so it is passed into a Date object so that the 4-digit year can be obtained via an object method.

                
                  function getHM(movie) {
                    return '' + Math.floor(movie.runtime / 60) + 'h ' + (movie.runtime % 60) + 'm';
                  }
                
              

This function is used to obtain the runtime of a film in an "Xh Ym" format. It is called by the function that gets fired when the user clicks the "More Info" button for a movie on the app page.

The runtime data is obtained from a passed-in object that was created from a call to the TMDB API for details on one particular film. Math and string concatenation are used to return the runtime in the desired format.