Pjotrs Osnovskis
Pjotrs Osnovskis

Reputation: 103

How to move transform from whole container to its part?

I'm on this for a while now and cant figure it out. Basically, when I hover the "movie" it shows its decription. Nice. Now I want it to flow up from the bottom of the image so movie name and score would stay visible, so I can add a link to the movie name and open its full details and trailer etc as a separate page.

Here's my code:

// API Key and URL for The Movie DB

const APIKey = "84f767ca64c6a454244f1189da5728db";

const APIURL = "https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=84f767ca64c6a454244f1189da5728db&page-1";
const IMGPath = "https://image.tmdb.org/t/p/w1280";
const SearchAPI = "https://api.themoviedb.org/3/search/movie?&api_key=84f767ca64c6a454244f1189da5728db&query=";

const main = document.getElementById("main");
const form = document.getElementById("form");
const search = document.getElementById("search");


// calling getMovies by popularity function
getMovies(APIURL);

// getMovies function, it takes movies list from API and iterates through it so can be displayed
async function getMovies(url) {
  const resp = await fetch(url);
  const respData = await resp.json();

// using function to show popular movies
  moviesSearchResults(respData.results);
}

//function shows movies client searched for and used to show popular movies
function moviesSearchResults(movies) {

// clearing main to show new results
  main.innerHTML = "";


  movies.forEach((movie) => {
    let { poster_path, title, vote_average, overview} = movie;


    const movieEl = document.createElement("div");
    movieEl.classList.add("movie");

// HTML template to be used for each individual movie with image, titile and score
    movieEl.innerHTML = `
        <div class="movie-image">
          <img 
            src="${IMGPath + poster_path}" 
            alt="${title}"
            class="movie-img"
          />
          <div class="description">
            <h4>${title}</h4>
            ${overview}
            <button type="button" id="info-btn" src="#">More Info...</button>
          </div>
        </div>
        <div class="movie-info">
          <h3>${title}</h3>
          <span class="${getClassByRate(vote_average)}">${vote_average}</span>
        </div>
      
    `;

    main.appendChild(movieEl);
  });

}



// function to mark score with certain colour at relevant score.
function getClassByRate(vote) {
  if(vote >= 8) {
    return "green";
  } else if (vote >= 5) {
    return "orange";
  } else {
    return "red";
  }
}


// Event listener for search submit
form.addEventListener("submit", (i) => {
  i.preventDefault();

  const searchTerm = search.value;

  if (searchTerm) {
    getMovies(SearchAPI + searchTerm);

    search.value = "";
  }
});
/* Fonts */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@100&display=swap');


/* Main page styles */

* {
  box-sizing: border-box;
}

body {
  background-color: #C76080;
  font-family: 'Poppins', sans-serif;
  margin: 0;
}

main {
   display: flex;
   flex-wrap: wrap;
   justify-content: center;

}

/* Movie box */

.movie {
  background-color: #373b69;
  border-radius: 3px;
  box-shadow: 0 4px 5px rgba(0, 0, 0, 0.2);
  overflow: hidden;
  width: 250px;
  margin: 1rem;
  position: relative;
}

.movie img {
  max-width: 100%;
  display: block;
}

.movie-info {
  color: #eee;
  display: flex;
  justify-content: space-between;
  padding: 0.5rem 1rem 1rem;
  letter-spacing: 0.5px;
  align-items: center;
  font-size: 0.8rem;

}

.movie-info h3 {
  margin: 0;
}

.movie-info span {
  background-color: #22254b;
  padding: 0.25rem 0.5rem;
  border-radius: 3px;
  font-weight: bold;
}

.movie-info span.green {
  color: rgb(39, 189, 39);
}
.movie-info span.orange {
  color: orange;
}
.movie-info span.red {
  color: rgb(189, 42, 42);
}


/* Search bar */

header {
  background-color: #373b69;
  padding: 1.5rem;
  display: flex;
  justify-content: end;
}

.search {
  padding: 0.5rem 1rem;
  border-radius: 15px;
  background-color: transparent;
  border: 2px solid #22254b;
  font-family: inherit;
  font-weight: bold;
  font-size: 1rem;
  color: #eee;
}

.search:focus {
  outline: none;
}

.search::placeholder {
  color: #eee;
  letter-spacing: 0.5px;
}


/* Overview styling */


.description {
  position: absolute;
  left: 0;
  bottom: 0;
  right: 0;
  background-color: #FFBB62;
  padding: 2rem;
  transform: translateY(101%);
  font-weight: bold;
  transition: transform 0.4s ease-in;
/* here is to set scrolling */
  max-height: 100%;
  overflow: auto;
  
}

.description h4 {
  margin: 0;
  padding: 1rem 0;
  text-decoration: underline;
}

.movie:hover .description {
  transform: translateY(0);
}
/* descriotion scroll bar */

/* scroll bar width */
.description::-webkit-scrollbar {
  width: 5px;
}

/* scroll bar track */
.description::-webkit-scrollbar-track {
  box-shadow: inset 0 0 5px grey;
  border-radius: 5px;
}

/* scroll bar handle */
.description::-webkit-scrollbar-thumb {
  background: #C76080;
  border-radius: 5px;
}

/* More info button styling */

#info-btn {
  margin-top: 1rem;
  padding: 0.2rem 0.6rem;
  border-radius: 10px;
  background-color:  #C76080;
  border: 1px solid #22254b;
  font-family: inherit;
  font-weight: bold;
  font-size: 1rem;
  color: #eee;
}

#info-btn:hover {
  background-color:  #C76080b0;

}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Movie App</title>
  <script src="assets/js/script.js" defer></script>
  <link rel="stylesheet" href="assets/css/style.css" />
</head>

<body>
  <header>
    <form id="form">
      <input 
        type="text"
        id="search"
        class="search"
        placeholder="Search" 
      />
    </form>
  </header>
  <main id="main">
  </main>
</body>

</html>

Upvotes: 1

Views: 87

Answers (1)

m4n0
m4n0

Reputation: 32255

You need to adjust the translateY property after hover. Add some extra padding to the description on top after it is hovered on.

Edit:

I also observed that you have a dynamic height for the movie info so I just tried writing a vanilla JS code for the same. Please check the code with comments at the end of JS part. You can easily link the title with the hyperlink associated with your dynamic variable.

// API Key and URL for The Movie DB

const APIKey = "84f767ca64c6a454244f1189da5728db";

const APIURL = "https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=84f767ca64c6a454244f1189da5728db&page-1";
const IMGPath = "https://image.tmdb.org/t/p/w1280";
const SearchAPI = "https://api.themoviedb.org/3/search/movie?&api_key=84f767ca64c6a454244f1189da5728db&query=";

const main = document.getElementById("main");
const form = document.getElementById("form");
const search = document.getElementById("search");


// calling getMovies by popularity function
getMovies(APIURL);

// getMovies function, it takes movies list from API and iterates through it so can be displayed
async function getMovies(url) {
  const resp = await fetch(url);
  const respData = await resp.json();

  // using function to show popular movies
  moviesSearchResults(respData.results);
}

//function shows movies client searched for and used to show popular movies
function moviesSearchResults(movies) {

  // clearing main to show new results
  main.innerHTML = "";


  movies.forEach((movie) => {
    let {
      poster_path,
      title,
      vote_average,
      overview
    } = movie;


    const movieEl = document.createElement("div");
    movieEl.classList.add("movie");

    // HTML template to be used for each individual movie with image, titile and score
    movieEl.innerHTML = `
        <div class="movie-image">
          <img 
            src="${IMGPath + poster_path}" 
            alt="${title}"
            class="movie-img"
          />
          <div class="description">
            <h4>${title}</h4>
            ${overview}
            <button type="button" id="info-btn" src="#">More Info...</button>
          </div>
        </div>
        <div class="movie-info">
          <h3><a href="https://weboas.is/" target="_blank">${title}</h3>
          <span class="${getClassByRate(vote_average)}">${vote_average}</span>
        </div>
      
    `;

    main.appendChild(movieEl);
  });

}



// function to mark score with certain colour at relevant score.
function getClassByRate(vote) {
  if (vote >= 8) {
    return "green";
  } else if (vote >= 5) {
    return "orange";
  } else {
    return "red";
  }
}


// Event listener for search submit
form.addEventListener("submit", (i) => {
  i.preventDefault();

  const searchTerm = search.value;

  if (searchTerm) {
    getMovies(SearchAPI + searchTerm);

    search.value = "";
  }
});

// Set dynamic translate based on card and image height difference

setTimeout(() => {
  const movieCard = document.querySelectorAll(".movie");

  movieCard.forEach((info) => {
    const infoStyles = window.getComputedStyle(info); // Get the computer styles of the card
    const cardHeight = infoStyles.height;
    const cardImage = info.querySelector(".movie-image")
    const imageStyles = window.getComputedStyle(cardImage);
    const imageHeight = imageStyles.height;

    const infoHeight = parseInt(cardHeight) - parseInt(imageHeight); // Convert px to number

    const description = info.querySelector(".movie-image .description");
    const movieInfo = info.querySelector(".movie-info");
    const translateYsymbol = parseInt(infoStyles.height);

    info.addEventListener("mouseenter", () => { // On hover
      description.style.setProperty("transform", "translateY(" + -infoHeight + "px)") // Negative translateY
    })
    info.addEventListener("mouseleave", () => { // When not hovered
      description.style.setProperty("transform", "translateY(100%)")
    })
  })
}, 3000);
/* Fonts */

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@100&display=swap');

/* Main page styles */

* {
  box-sizing: border-box;
}

body {
  background-color: #C76080;
  font-family: 'Poppins', sans-serif;
  margin: 0;
}

main {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}


/* Movie box */

.movie {
  background-color: #373b69;
  border-radius: 3px;
  box-shadow: 0 4px 5px rgba(0, 0, 0, 0.2);
  overflow: hidden;
  width: 250px;
  margin: 1rem;
  position: relative;
}

.movie img {
  max-width: 100%;
  display: block;
}

.movie-info {
  color: #eee;
  display: flex;
  justify-content: space-between;
  padding: 0.5rem 1rem 1rem;
  letter-spacing: 0.5px;
  align-items: center;
  font-size: 0.8rem;
}

.movie .description {
  padding-top: 5em;
}

.movie-info h3 {
  margin: 0;
}

.movie-info a {
  color: #fff;
  text-decoration: none;
}

.movie-info span {
  background-color: #22254b;
  padding: 0.25rem 0.5rem;
  border-radius: 3px;
  font-weight: bold;
}

.movie-info span.green {
  color: rgb(39, 189, 39);
}

.movie-info span.orange {
  color: orange;
}

.movie-info span.red {
  color: rgb(189, 42, 42);
}


/* Search bar */

header {
  background-color: #373b69;
  padding: 1.5rem;
  display: flex;
  justify-content: end;
}

.search {
  padding: 0.5rem 1rem;
  border-radius: 15px;
  background-color: transparent;
  border: 2px solid #22254b;
  font-family: inherit;
  font-weight: bold;
  font-size: 1rem;
  color: #eee;
}

.search:focus {
  outline: none;
}

.search::placeholder {
  color: #eee;
  letter-spacing: 0.5px;
}


/* Overview styling */

.description {
  position: absolute;
  left: 0;
  bottom: 0;
  right: 0;
  background-color: #FFBB62;
  padding: 2rem;
  transform: translateY(101%);
  font-weight: bold;
  transition: transform 0.4s ease-in;
  /* here is to set scrolling */
  max-height: 100%;
  overflow: auto;
}

.description h4 {
  margin: 0;
  padding: 1rem 0;
  text-decoration: underline;
}


/* descriotion scroll bar */


/* scroll bar width */

.description::-webkit-scrollbar {
  width: 5px;
}


/* scroll bar track */

.description::-webkit-scrollbar-track {
  box-shadow: inset 0 0 5px grey;
  border-radius: 5px;
}


/* scroll bar handle */

.description::-webkit-scrollbar-thumb {
  background: #C76080;
  border-radius: 5px;
}


/* More info button styling */

#info-btn {
  margin-top: 1rem;
  padding: 0.2rem 0.6rem;
  border-radius: 10px;
  background-color: #C76080;
  border: 1px solid #22254b;
  font-family: inherit;
  font-weight: bold;
  font-size: 1rem;
  color: #eee;
}

#info-btn:hover {
  background-color: #C76080b0;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Movie App</title>
  <script src="assets/js/script.js" defer></script>
  <link rel="stylesheet" href="assets/css/style.css" />
</head>

<body>
  <header>
    <form id="form">
      <input type="text" id="search" class="search" placeholder="Search" />
    </form>
  </header>
  <main id="main">
  </main>
</body>

</html>

Codepen Demo: https://codepen.io/Manoj6994/pen/BaRzxqZ

Note that I have included the setTimeout for it to wait for the API to finish the job. You may want to call those scripts after the API requests like this: Execute code after 2 API calls response

Upvotes: 1

Related Questions