0

I am trying to get data from each movieCard into an object and add it to an array to use in local storage to make a watchlist. When I try to add a for loop to go through the buttons I only get the data from the last button. I'm not sure how to grab the data for each card. I am still pretty new to javascript so any help would be appreciated.

//api key
const apikey = ''

//grab DOM elements
const searchInput = document.getElementById('input-el')
const searchBtn = document.getElementById('button-el')
const movieList = document.getElementById('movies')

//array of objects that makes up watchlist
let watchlist = []

//determine whether to display add or remove button 
let addRemoveWatchlist = ''

//fetch api data if search input is truthy
const fetchMovies = async(searchTerm) => {
    const URL = `http://www.omdbapi.com/?apikey=${apikey}&s=${searchTerm}`
    const res = await fetch(`${URL}`)
    const movies = await res.json()
    if(movies.Response == 'True') {
        displayMovieList(movies.Search)
    } else {
        movieList.innerHTML = `
            <section class='movie-card'>
                We can't seem to find anything . . . try being more specific
            </section>
            `
    }
}

const fetchMoreInfo = async(movieId) => {
    const URL = `http://www.omdbapi.com/?apikey=${apikey}&i=${movieId}`
    const res = await fetch(`${URL}`)
    const moreInfo = await res.json()
    // console.log(moreInfo)
    return moreInfo 
}

//loop through the api data array and display in html
const displayMovieList = async(movies) => {
    movieList.innerHTML = ''
    let movieCard = document.createElement('div')
    movies.forEach(async (movie) => { 
        let data = await fetchMoreInfo(movie.imdbID)
        movieCard = 
                    `<section class='movie-card'>
                        <img src='${data.Poster}' class='movie-img' />
                        <div class='movie-info'>
                            <section class='section1'>
                                <div class='movie-title'>${data.Title}</div>
                                <div class='movie-year'>${data.Year}</div>
                                <div class='imdb-rating'><i class="fa-solid fa-star"></i>${data.imdbRating}</div>
                            </section>
                            <section class='section2'>
                                <div class='movie-rating'><b>${data.Rated}</b></div>
                                <div class='movie-runtime'>${data.Runtime}</div>
                                <button class='watchlist'>Add to Watchlist</button>
                            </section>
                            <section class='section3'>
                                <div class='actors'><b>Actors:</b> ${data.Actors}</div>
                                <div class='director'><b>Director:</b> ${data.Director}</div>
                                <div class='plot'>${data.Plot}</div>
                            </section>
                        </div>
                    </section>
                    <hr>`       
        movieList.innerHTML += movieCard
               
})

const watchlistEl = document.querySelectorAll('.watchlist')
    for (let i = 0; i < watchlistEl.length; i++) {
        watchlistEl[i].addEventListener('click', () => {
            watchlist.push(data.Title)
            console.log(watchlist)
        })
    }
}

//grab search bar input
const findMovies = ()  => {
    let searchTerm = searchInput.value
    fetchMovies(searchTerm)
    
}

searchBtn.addEventListener('click', findMovies)

searchInput.addEventListener('keypress', (e) => {
    if (e.key === 'Enter') {
        searchBtn.click()
    }
})
p_bowles
  • 3
  • 3
  • 2
    "*When I try to add a for loop to go through the buttons*" where is the `for` loop (*this is not the same thing as `forEach`*)? And where is the code that works with your buttons? The buttons in this code have no actions/events so they will do nothing. If you have code that sets event listeners on the buttons, you need to include that. We cannot make guesses or assumptions about your code if we are going to help. – EssXTee Sep 02 '22 at 13:27
  • I've added back the for loop that i was trying to use – p_bowles Sep 02 '22 at 14:38
  • The line where you set `const watchlistEl = document.getElementsByClassName('watchlist')` happens ***before*** the buttons are added to the page. And your `for` loop is happening inside of the `forEach` loop, which means it runs that loop multiple times (rather than once, after all buttons have been created). You should set the value of `watchlistEl` ***after*** the `forEach` loop has added all of the buttons to the page, and then run your `for` loop to set the event listeners. – EssXTee Sep 02 '22 at 14:43

1 Answers1

0

I covered this in my comment, but here is an actual answer version.

Essentially the watchlistEl variable was being set before the buttons were added to the page. Additionally, the code that loops through watchlistEl to set the event listeners was happening inside of the forEach loop, meaning it ran multiple times (for each item added to the page).

So the solution would be to set watchlistEl after all of the items have been added (after the forEach loop), and then loop one time at the end to set the event listeners.

const displayMovieList = async (movies) => {
  movieList.innerHTML = ''
  let movieCard = document.createElement('div')
  for await (const movie of movies) {
    let data = await fetchMoreInfo(movie.imdbID)
    movieCard = `<section class='movie-card'>
      <img src='${data.Poster}' class='movie-img' />
        <div class='movie-info'>
          <section class='section1'>
            <div class='movie-title'>${data.Title}</div>
            <div class='movie-year'>${data.Year}</div>
            <div class='imdb-rating'>
              <i class="fa-solid fa-star"></i>${data.imdbRating}</div>
          </section>
          <section class='section2'>
            <div class='movie-rating'><b>${data.Rated}</b></div>
            <div class='movie-runtime'>${data.Runtime}</div>
            <button class='watchlist'>Add to Watchlist</button>
          </section>
          <section class='section3'>
            <div class='actors'><b>Actors:</b> ${data.Actors}</div>
            <div class='director'><b>Director:</b> ${data.Director}</div>
            <div class='plot'>${data.Plot}</div>
          </section>
        </div>
      </section>
      <hr>`;
    movieList.innerHTML += movieCard;
  }
  
  const watchlistEl = document.querySelectorAll('.watchlist');
  watchlistEl.forEach(wl => {
    wl.addEventListener('click', e => {
      // We have to get the title from its element, relative to the button clicked
      watchlist.push(e.target.parentElement.parentElement.querySelector(".movie-title").innerText);
      console.log(watchlist);
    });
  });
}

NOTE

Your code is using a Promise to get the data. The code that fetches the data is not included but this may affect when the event listeners need to be set. Chances are if this code does not work, you'll need to set the event listeners after the Promise that fetches the data is resolved.

Using async/await with a forEach loop actually covers ways to handle this, but I will not integrate that solution as there are too many unknowns with OPs code (where does displayMovieList get called and what does it return?, what does fetchMoreInfo do and what does it return?)

EDIT

After updated code from OP, I have modified this to use a for await...of loop instead, which allows all movie information to be pulled first, then the code to set event listeners on the button runs.

EssXTee
  • 1,783
  • 1
  • 13
  • 18
  • I added the rest of my code, still new to stack overflow. I tried what you did and it didn't return anything. I assume it runs before the data is there. – p_bowles Sep 02 '22 at 16:34
  • "*didn't return anything*" What didn't return anything? No movie data? The code I added/changed isn't supposed to return anything, it only loops through the `watchlist` buttons. Your original post is lacking details (like the `displayMovieList` and `fetchMoreInfo` functions), which leads to anyone who wants to answer having to make guesses and assumptions about what the rest of your code does. If it is not returning/displaying movie data, that is in the rest of your code, not what was posted here. If it is not updating the buttons, that is probably because `forEach` does not await. – EssXTee Sep 02 '22 at 17:42
  • @p_bowles Based on the new code you have provided I have updated my snippet. It should loop through all the movies and update the page first, then set event listeners on the buttons. Also, if that is your actual API key in your post, I would remove that part. I only needed to see how those two additional functions worked to make sure my answer would work as well. – EssXTee Sep 02 '22 at 18:11