Custom native HTML, CSS and JS video player. Completely native no library is needed. Colors can be changed from CSS easily.
Enjoy using. Alternative ->
See the Pen Untitled by sinanisler (@sinanisler) on CodePen.
<div class="video-container">
<video id="myVideo">
<source src="https://wpaimuse.com/wp-content/uploads/2024/03/2024-03-14-11-59-41-2.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
<!-- Custom Controls -->
<div class="controls">
<button id="playPauseBtn">►</button>
<div class="progress-container">
<div id="progressBar" class="progress-bar">
<div id="progress" class="progress"></div>
<!-- Progress dots will be added here -->
</div>
</div>
<button id="muteBtn">🔈</button>
<button id="fullscreenBtn">⛶</button>
</div>
<!-- Chapter buttons -->
<div id="chapterButtons" class="chapter-buttons" style="display:none"></div>
</div>
/* Container styling */
.video-container {
position: relative;
width: 640px;
margin: 0 auto;
background: #000;
}
/* Video styling */
#myVideo {
width: 100%;
}
/* Custom controls styling */
.controls {
display: flex;
align-items: center;
justify-content: space-between;
background: #333;
padding: 10px;
color: #fff;
}
.controls button {
background: none;
border: none;
color: #fff;
font-size: 20px;
cursor: pointer;
}
/* Progress bar container */
.progress-container {
flex: 1;
margin: 0 10px;
position: relative;
height: 10px;
background: #555;
cursor: pointer;
}
/* Progress bar */
.progress-bar {
position: relative;
width: 100%;
height: 100%;
}
.progress {
background: #2196F3;
height: 100%;
width: 0%;
}
/* Progress dots */
.progress-dot {
position: absolute;
top: 50%;
width: 8px;
height: 8px;
background: #fff;
border-radius: 50%;
cursor: pointer;
transform: translate(-50%, -50%);
}
/* Tooltip styling */
.tooltip {
position: absolute;
padding: 5px 10px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 4px;
font-size: 14px;
white-space: nowrap;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s;
}
/* Chapter buttons styling */
.chapter-buttons {
margin-top: 20px;
text-align: center;
}
.chapter-buttons button {
margin: 5px;
padding: 10px;
font-size: 16px;
cursor: pointer;
}
// Get the video element
const video = document.getElementById('myVideo');
// Array of chapters with title and time (in minutes:seconds)
const chapters = [
{ title: "Chapter 1", time: "0:30" },
{ title: "Chapter 2", time: "1:00" },
{ title: "Chapter 3", time: "1:30" },
{ title: "Chapter 4", time: "2:00" }
];
// Convert "min:sec" to total seconds
function timeToSeconds(time) {
const [minutes, seconds] = time.split(':').map(Number);
return minutes * 60 + seconds;
}
// Jump to a specific time in the video
function goToTime(seconds) {
video.currentTime = seconds;
video.play();
}
// Generate chapter buttons
const chapterButtonsDiv = document.getElementById('chapterButtons');
chapters.forEach((chapter) => {
const button = document.createElement('button');
button.textContent = `${chapter.title} - ${chapter.time}`;
button.addEventListener('click', () => {
const seconds = timeToSeconds(chapter.time);
goToTime(seconds);
});
chapterButtonsDiv.appendChild(button);
});
// Custom Controls
const playPauseBtn = document.getElementById('playPauseBtn');
const muteBtn = document.getElementById('muteBtn');
const fullscreenBtn = document.getElementById('fullscreenBtn');
const progressContainer = document.querySelector('.progress-container');
const progressBar = document.getElementById('progress');
const progressBarContainer = document.getElementById('progressBar');
// Tooltip element
const tooltip = document.createElement('div');
tooltip.className = 'tooltip';
document.body.appendChild(tooltip);
// Play/Pause functionality
playPauseBtn.addEventListener('click', () => {
if (video.paused) {
video.play();
playPauseBtn.textContent = '❚❚';
} else {
video.pause();
playPauseBtn.textContent = '►';
}
});
// Mute/Unmute functionality
function updateMuteButton() {
muteBtn.textContent = video.muted ? '🔈+' : '🔈-';
}
// Initial mute button state
updateMuteButton();
muteBtn.addEventListener('click', () => {
video.muted = !video.muted;
updateMuteButton();
});
// Fullscreen functionality
fullscreenBtn.addEventListener('click', () => {
if (video.requestFullscreen) {
video.requestFullscreen();
} else if (video.webkitRequestFullscreen) { /* Safari */
video.webkitRequestFullscreen();
} else if (video.msRequestFullscreen) { /* IE11 */
video.msRequestFullscreen();
}
});
// Update progress bar as the video plays
video.addEventListener('timeupdate', () => {
const percent = (video.currentTime / video.duration) * 100;
progressBar.style.width = percent + '%';
});
// Seek functionality
progressContainer.addEventListener('click', (e) => {
const rect = progressContainer.getBoundingClientRect();
const offsetX = e.clientX - rect.left;
const totalWidth = rect.width;
const percent = offsetX / totalWidth;
video.currentTime = percent * video.duration;
});
// Create progress dots after metadata is loaded
function createProgressDots() {
chapters.forEach((chapter) => {
const dot = document.createElement('div');
dot.classList.add('progress-dot');
const seconds = timeToSeconds(chapter.time);
const percent = (seconds / video.duration) * 100;
dot.style.left = percent + '%';
// Store the chapter title for tooltip
dot.dataset.title = chapter.title;
// Add event listeners for tooltip
dot.addEventListener('mouseenter', (e) => {
tooltip.textContent = chapter.title;
tooltip.style.opacity = 1;
const rect = dot.getBoundingClientRect();
tooltip.style.left = rect.left + 'px';
tooltip.style.top = rect.top - 30 + 'px'; // Position above the dot
});
dot.addEventListener('mouseleave', () => {
tooltip.style.opacity = 0;
});
dot.addEventListener('click', (e) => {
e.stopPropagation(); // Prevent triggering the parent click event
goToTime(seconds);
});
progressBarContainer.appendChild(dot);
});
}
// Wait for video metadata to be loaded before creating progress dots
if (video.readyState >= 1) {
createProgressDots();
} else {
video.addEventListener('loadedmetadata', createProgressDots);
}
// Update Play/Pause button text based on video state
video.addEventListener('play', () => {
playPauseBtn.textContent = '❚❚';
});
video.addEventListener('pause', () => {
playPauseBtn.textContent = '►';
});
// Update mute button when video mute state changes
video.addEventListener('volumechange', updateMuteButton);
Version 2:
See the Pen Untitled by sinanisler (@sinanisler) on CodePen.