Custom HTML5 Video Player with Chapters and Tooltips, No Lib ๐Ÿ˜Ž

Custom native HTML, CSS and JS video player. Completely native no library is needed. Colors can be changed from CSS easily.

Enjoy using. Alternative ->

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<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>
<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>
  <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>
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
/* 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;
}
/* 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; }
    /* 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;
    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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);
// 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);
    // 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);

Leave the first comment

Hello ๐Ÿ– Welcome

I am planning a professional WordPress and Bricks Builder course. If you are Interested register to this newsletter.