Film Finder - Deleting liked/disliked movies from page and local storage

I’ve been expanding on the Film finder project, namely by adding the liked and disliked movies in a side bar and in the local storage.

Now I’m facing an issue to delete the movies individually, both from the page and from the local storage.
(I’m trying to make this work for the liked movies only right now to avoid too much code)

This is the flow of the program once the liked button is clicked:

  1. likeMovie(movieInfo)addToLikedMovies(movieInfo)
  2. addToLikedMovies(movieInfo)storeLikedMovie(movieInfo)
  3. addToLikedMovies(movieInfo)displayLikedMovies()
  4. displayLikedMovies()createLikedMovie(movie) —> argument is each movie from the stored array of liked movies
  5. createLikedMovie(movie)
    1. creates the delete button for the movie
    2. On clicking the delete button: deleteMovieFromPage()
    3. On clicking the delete button: deleteFromLocalStorage(movie)

Initially, I was looping over the delete buttons outside of the createLikedMovie() function and I had attached an event listener to remove each movie from the page when targeted. But with that method, I was only able to pass the node to the function, while I also want to remove it also from the local storage, and for this I would need to pass the movie object/data. So I thought best to attach the handlers inside the createLikedMovie() within which the delete button is created for each movie, because that function takes the movie object as an argument which I can then use to remove the movie from the LS. However, that’s where I’m getting problems.

With the code below, I get zero error in the console, but only the first movie of the list can be removed from the page, and secondly the movie isn’t found in the stored array of liked movies, so cannot be removed from the local storage.

In my helpers.js:

// After liking a movie, clears the current movie from the screen, gets another random movie and adds to list of liked movies
const likeMovie = (movieInfo) => {
  clearCurrentMovie();
  showRandomMovie();
  addToLikedMovies(movieInfo);
};

// Add movie to list of liked movies
const addToLikedMovies = (movieInfo) => {
  storeLikedMovie(movieInfo);
  displayLikedMovies();
};

// Add liked movies to local storage
const storeLikedMovie = (movieInfo) => {
  let myLikedMovies = localStorage.getItem('likedMovies')
    ? JSON.parse(localStorage.getItem('likedMovies'))
    : [];
  myLikedMovies.push(movieInfo);
  localStorage.setItem('likedMovies', JSON.stringify(myLikedMovies));
};

// Show liked movies in sideBar
const displayLikedMovies = () => {
  const movieList = document.getElementById('likedMoviesList');
  if (movieList.hasChildNodes()) {
    while (movieList.firstChild) {
      movieList.removeChild(movieList.firstChild);
    }
  }
  let likedMovies = JSON.parse(localStorage.getItem('likedMovies'));
  likedMovies.forEach((movie) => createLikedMovie(movie));
};

// Create HTML for liked movies
const createLikedMovie = (movie) => {
  const movieList = document.getElementById('likedMoviesList');
  const title = document.createElement('li');
  title.classList.add('likedMovie');
  title.setAttribute('id', 'likedMovie');
  title.innerHTML = `${movie.title} <i class="fa-solid fa-circle-minus delete-btn"></i>`;
  movieList.appendChild(title);

  const deleteBtn = movieList.querySelector('.delete-btn');
  deleteBtn.addEventListener('click', deleteMovieFromPage); // only works for the first movie of the list
  deleteBtn.addEventListener('click', (movie) => deleteFromLocalStorage(movie)); // see comments below
};

const deleteMovieFromPage = (e) => {
  const movieEl = e.currentTarget.parentNode;
  movieEl.remove(); 
};

const deleteFromLocalStorage = (movie) => {
  const myLikedMovies = JSON.parse(localStorage.getItem('likedMovies'));
  const movieIndex = myLikedMovies.indexOf(movie);
  console.log(myLikedMovies);
  console.log(movie); // The movie does show up in the liked movies array logged above
  console.log(movieIndex); // yet this returns -1, so not found in myLikedMovies
  myLikedMovies.splice(movieIndex, 1); // so this results in incorrect movie being deleted
  localStorage.setItem('likedMovies', JSON.stringify(myLikedMovies));
};

In script.js, I load the movies previously saved to the local storage and create the movies anew from there:

const myLikedMovies = JSON.parse(localStorage.getItem('likedMovies'));
const myDislikedMovies = JSON.parse(localStorage.getItem('dislikedMovies'));

if (myLikedMovies) {
  myLikedMovies.forEach((movie) => createLikedMovie(movie));
}

if (myDislikedMovies) {
  myDislikedMovies.forEach((movie) => createDislikedMovie(movie));
}

I’ve tried different ways (also modifying deleteMovieFromPage() just a little):

This:

const deleteMovieFromPage = (movieEl) => {
  movieEl.remove();
};

// Inside createLikedMovie()
deleteBtn.onclick = (e) => {
     deleteMovieFromPage(e.currentTarget.parentNode);
     deleteFromLocalStorage(movie);
   };

Also this:

const deleteMovieFromPage = (movieEl) => {
  movieEl.remove();
};

// Inside createLikedMovie()
deleteBtn.onclick = (e) => deleteMovieFromPage(e.currentTarget.parentNode);
deleteBtn.onclick = (movie) => deleteFromLocalStorage(movie);

And this:

const deleteMovieFromPage = (movieEl) => {
  movieEl.remove();
};

// Inside createLikedMovie()
deleteBtn.addEventListener('click', (e) => {
    deleteMovieFromPage(e.currentTarget.parentNode);
    deleteFromLocalStorage(movie);
   });

In the end it seems that it would be preferable to use addEventListener instead of onclick, but I’m getting the same behaviour with both.

The full code can be found here: https://www.codecademy.com/workspaces/626e4eceac46cff76f1a1658

After much console.logging, reading about event handlers and StackOverflow, if someone has time to point me to the right direction, it would be much appreciated :pray:

In order to have some sort of connection between the li element and the movie object,
you may want to change this:
In the createLikedMovie function,
change the id to contain information that could be used to identify which movie object goes with it:

title.setAttribute('id', 'likedMovie' + movie.id); 

Also, notice that the first argument for an event listener is the event object
So,

deleteBtn.addEventListener('click', (movie) => deleteFromLocalStorage(movie));

won’t work as expected because movie is being treated as an event object there.

If you change the the id as I suggested,
and you change the deleteFromLocalStorage function to have an argument that’s the movie’s id instead of a movie object,
you could do:

deleteBtn.addEventListener('click', (event) => {
  const button = event.currentTarget;
  const li = button.parentElement; 
  const id = li.id;  // alternatively, can use title.id; and leave previous 2 lines out
  const movieId = Number(id.substring(id.indexOf("Movie") + 5));
  deleteFromLocalStorage(movieId);
});

I guess a shorter version of that would be:

const movieId = movie.id;
deleteBtn.addEventListener('click', () => { deleteFromLocalStorage(movieId); });

Here’s one way to adjust deleteFromLocalStorage so that an parameter that’s a movie’s id would work:
instead of using .indexof for the array, use .findIndex to find the movie object for which the .id matched the parameter (which would be the movie’s id).

But I’m not clear on how the function would know to delete from the likedMovies instead of the dislikedMovies.
Does it just try to delete it from both?

1 Like

The reason the event listener is only being assigned to the first movie is this:
You have

const deleteBtn = movieList.querySelector('.delete-btn');

but this only selects the first element within the list of movies that has class delete-btn.
You only want to select the appropriate element inside the list item element (which you called title).
Try:

const deleteBtn = title.querySelector('.delete-btn');
1 Like

you have a similar issue in the deleteMovieFromPage function
here:

const deleteBtn = document.querySelector('.delete-btn');

One way to fix that is to replace that line with

const deleteBtn = e.currentTarget;
1 Like

Hi Jan, I don’t have time right now to go back to the project with your answers in mind but: thank you thank you (again) for helping me out! :slight_smile: Really appreciate it. Will reply in more detail as soon as I have some coding time in front of me!

Hi Jan,

I finally had time to get back to this!
Indeed, great idea to use the movie’s id!

Also, notice that the first argument for an event listener is the event object
So,

deleteBtn.addEventListener('click', (movie) => deleteFromLocalStorage(movie));

won’t work as expected because movie is being treated as an event object there.

Ugh :face_with_peeking_eye: thanks for pointing that one out!

instead of using .indexof for the array, use .findIndex to find the movie object for which the .id matched the parameter (which would be the movie’s id).

That helped a lot! In the end, to make the code a bit shorter I used the filter method to return an array of all movies not matching the id of the movie to be removed.

but this only selects the first element within the list of movies that has class delete-btn.
You only want to select the appropriate element inside the list item element (which you called title).

Oh wow, I’m still trying to wrap my head around that one :see_no_evil:

But I’m not clear on how the function would know to delete from the likedMovies instead of the dislikedMovies .
Does it just try to delete it from both?

I hadn’t set up that logic yet because I was trying to make the code work for the liked movies first, before making it more complex. But the plan was to use the same function to delete either from the liked movies or the disliked movies, based on the class in the title. So in the end I also passed title to my deleteFromLocalStorage() function, along with the movie.id, to make that possible.

The final code looks like that:

// Create HTML for liked movies
const createLikedMovie = (movie) => {
  const movieList = document.getElementById('likedMoviesList');
  const title = document.createElement('li');
  title.classList.add('likedMovie');
  title.setAttribute('id', 'likedMovie');
  title.innerHTML = `${movie.title} <i class="fa-solid fa-circle-minus delete-btn"></i>`;
  movieList.appendChild(title);

  const deleteBtn = title.querySelector('.delete-btn');
  deleteBtn.onclick = (e) => {
    deleteMovieFromPage(e);
    deleteFromLocalStorage(title, movie.id);
  };
};

// Create HTML for disliked movies
const createDislikedMovie = (movie) => {
  const movieList = document.getElementById('dislikedMoviesList');
  const title = document.createElement('li');
  title.classList.add('dislikedMovie');
  title.setAttribute('id', 'dislikedMovie');
  title.innerHTML = `${movie.title} <i class="fa-solid fa-circle-minus delete-btn"></i>`;
  movieList.appendChild(title);

  const deleteBtn = title.querySelector('.delete-btn');
  deleteBtn.onclick = (e) => {
    deleteMovieFromPage(e);
    deleteFromLocalStorage(title, movie.id);
  };
};

// Remove movie individually from page
const deleteMovieFromPage = (e) => {
  const movieEl = e.currentTarget.parentNode;
  movieEl.remove();
  const deleteBtn = e.currentTarget;
  deleteBtn.removeEventListener('click', deleteMovieFromPage);
};

// Remove movie individually from LS
const deleteFromLocalStorage = (movieTitle, movieId) => {
  if (movieTitle.classList.contains('likedMovie')) {
    const myLikedMovies = JSON.parse(localStorage.getItem('likedMovies'));
    const updatedLikedMovies = myLikedMovies.filter(
      (movie) => movie.id !== movieId
    );
    localStorage.setItem('likedMovies', JSON.stringify(updatedLikedMovies));
  }
  if (movieTitle.classList.contains('dislikedMovie')) {
    const myDislikedMovies = JSON.parse(localStorage.getItem('dislikedMovies'));
    const updatedDislikedMovies = myDislikedMovies.filter(
      (movie) => movie.id !== movieId
    );
    localStorage.setItem(
      'dislikedMovies',
      JSON.stringify(updatedDislikedMovies)
    );
  }
};

So happy that it works now :slight_smile:
Thank you again @janbazant1107978602 for your super valuable help! :raised_hands: