I’m working on a video container design using HTML, CSS, and JavaScript, and I want to implement a feature where scrolling automatically switches to the next video, similar to how YouTube Shorts behaves.
I have a basic design where I load a video, along with interaction buttons like follow, like, comment, and share. However, I need help with the JavaScript functionality that allows the video to change when the user scrolls past the current one.
Here’s my HTML structure:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive Shorts Design</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<!-- Video Section -->
<div class="video-container">
<video id="video-player" src="videos/video1.mp4" controls></video>
<!-- Follow Button -->
<button class="follow-button">Follow</button>
<!-- Interaction buttons on the video player -->
<div class="actions">
<div class="action-button like">
<span><i class="fa-regular fa-heart"></i></span>
<span>452</span>
</div>
<div class="action-button comment">
<span><i class="fa-regular fa-comment"></i></span>
<span>6</span>
</div>
<div class="action-button share">
<span><i class="fa-solid fa-share"></i></span>
<span>Share</span>
</div>
</div>
</div>
</div>
<script src="scripts.js"></script>
</body>
</html>
And here’s my CSS:
/* Global Styles */
body, html {
margin: 0;
padding: 0;
overflow-x: hidden;
}
.container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
overflow: hidden;
}
.video-container {
width: 100%;
height: 100%;
}
video {
width: 100%;
height: 100%;
object-fit: cover;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: Arial, sans-serif;
}
body {
background-color: #181818;
color: white;
}
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 20px;
height: 100vh;
}
/* Video Section */
.video-container {
width: 100%;
max-width: 400px;
height: 800px;
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
video {
width: 100%;
height: 100%;
border-radius: 10px;
position: relative;
}
/* Follow Button */
.follow-button {
position: absolute;
top: 10px;
right: 10px;
padding: 8px 12px;
background-color: #ff0000;
color: white;
border: none;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s ease;
}
.follow-button:hover {
background-color: #cc0000;
}
/* Actions Section: Buttons on the right side of the video */
.actions {
position: absolute;
right: 10px;
top: 20%;
display: flex;
flex-direction: column;
align-items: center;
}
.action-button {
margin: 10px 0;
color: white;
text-align: center;
cursor: pointer;
padding: 10px;
background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent background */
border-radius: 50%;
width: 50px;
height: 50px;
opacity: 0.8; /* Transparency effect */
transition: opacity 0.3s ease;
}
.action-button:hover {
opacity: 1;
}
/* Ensure icons are centered within the buttons */
.action-button span {
display: block;
}
/* Subscribe Section */
.subscribe-section {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #333;
padding: 10px 15px;
border-radius: 10px;
max-width: 400px;
width: 100%;
margin-top: 10px;
}
.profile-pic {
width: 50px;
height: 50px;
border-radius: 50%;
}
.subscribe-info {
flex-grow: 1;
padding-left: 10px;
}
.subscribe-button {
padding: 10px 15px;
background-color: #ff0000;
border: none;
border-radius: 5px;
color: white;
cursor: pointer;
}
/* Responsive Media Queries */
@media (max-width: 768px) {
.actions {
right: 5px;
top: 15%;
}
.subscribe-section {
flex-direction: column;
align-items: center;
padding: 15px;
}
.profile-pic {
width: 40px;
height: 40px;
}
.subscribe-button {
margin-top: 10px;
width: 100%;
}
}
@media (max-width: 480px) {
.video-container {
max-width: 100%;
}
.actions {
position: static;
flex-direction: row;
width: 100%;
justify-content: space-evenly;
}
}
The idea is to switch between multiple videos automatically based on the user’s scroll interaction. I have an array of videos and need to load the next one when scrolling. Here’s the JavaScript I’ve tried:
document.addEventListener('DOMContentLoaded', function() {
const videos = [
"videos/video1.mp4",
"videos/video2.mp4",
"videos/video3.mp4",
// Add more videos as needed
];
let currentVideoIndex = 0;
const videoPlayer = document.getElementById('video-player');
// Function to load the next video
function loadNextVideo() {
currentVideoIndex++;
if (currentVideoIndex >= videos.length) {
currentVideoIndex = 0; // Loop back to the first video
}
videoPlayer.src = videos[currentVideoIndex];
videoPlayer.play();
}
// Using IntersectionObserver to detect when the user scrolls past the current video
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// If the current video is fully visible, play it
videoPlayer.play();
} else {
// If the video is not visible anymore, load the next video
loadNextVideo();
}
});
}, {
threshold: 0.75 // Video is considered visible when 75% of it is on screen
});
// Observing the video container
observer.observe(videoPlayer);
});
The Issue:
I want to achieve the same experience as YouTube Shorts, where each scroll jumps directly to the next video. However, I’m not sure if this approach with IntersectionObserver is the best for this case, or if there’s a better way to do this.
What would be the best way to implement this kind of scroll-based video switching?
Edit:
my updated js:
console.log("Scroller");
// Define the video array
const videos = [
"videos/video1.mp4",
"videos/video2.mp4",
"videos/video3.mp4",
// Add more videos as needed
];
// Get the container and video player elements
const container = document.querySelector('.container');
const videoPlayer = document.getElementById('video-player');
// Initialize the current video index
let currentVideoIndex = 0;
// Initialize a flag to indicate whether the user has interacted with the document
let userHasInteracted = false;
// Add the scroll event listener to the container
container.addEventListener('scroll', (e) => {
// Get the video offset and height
const videoOffset = videoPlayer.offsetTop;
const videoHeight = videoPlayer.offsetHeight;
// Get the scroll position and direction
const scrollPosition = container.scrollTop;
const scrollDirection = e.deltaY > 0 ? 'down' : 'up';
// Check if the user has scrolled down and the current video is no longer visible
if (scrollDirection === 'down' && scrollPosition > videoOffset + videoHeight - 20) {
// Load the next video
loadNextVideo();
}
});
// Function to load the next video
function loadNextVideo() {
// Increment the current video index
currentVideoIndex++;
// Loop back to the first video if we've reached the end of the array
if (currentVideoIndex >= videos.length) {
currentVideoIndex = 0;
}
// Update the video player source
videoPlayer.src = videos[currentVideoIndex];
// If the user has interacted with the document, play the video automatically
if (userHasInteracted) {
videoPlayer.play();
}
}
// Add an event listener to the document to listen for user interaction
document.addEventListener('click', () => {
// Set the flag to indicate that the user has interacted with the document
userHasInteracted = true;
// If the video is not playing, play it
if (!videoPlayer.paused) {
videoPlayer.play();
}
});
// Initialize the video player with the first video
videoPlayer.src = videos[0];