Reputation: 103
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
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