1

EDIT: I have found the solution, posted and marked as the best answer below.

I'm coding a portfolio website for myself using HTML, CSS and JS and I need to add multiple videos on a lot of pages.

I followed some tutorials to learn how to customize the video player, but it only works for one specific video on the page. If I were to add more videos, I'd need to have one .js custom player file for each video and manually select them on the website.

How can I apply this single .js custom video player to all of my videos using purely javascript?

I have found similar topics about the subject here, but all of them uses jQuery and I'm struggling to make it work with javascript.

My HTML for the video player:

<section class="videoplayer">
<div class="c-video">
    <div id="video_player">
        <video src="./media/portfolio/videos/Show-Reel-2021.mp4" id="main-video"></video>
        <div class="progressAreaTime">00:00</div>
        <div class="controls">
            <div class="progress-area">
                <div class="progress-bar">
                    <span></span>
                </div>
                <div class="buffered-progress-bar"></div>
            </div>
            <div class="controls-list">
                <div class="controls-left">
                    <span class="icon">
                        <i class="material-icons fast-rewind" title="Retroceder 10 segundos">first_page</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons play_pause" title="Reproduzir">play_arrow</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons fast-forward" title="Avançar 10 segundos">last_page</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons volume" title="Sem áudio">volume_up</i>
                        <input type="range" min="0" max="100" class="volume_range">
                    </span>
                    <div class="timer">
                        <span class="current">00:00</span> / <span class="duration">0:00</span>
                    </div>
                </div>
                <div class="controls-right">
                    <span class="icon">
                        <i class="material-icons auto-play" title="A repetição automática está desativada"></i>
                    </span>
                    <span class="icon">
                        <i class="material-icons settingsBtn" title="Detalhes">settings</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons picture_in_picture" title="Miniplayer">picture_in_picture_alt</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons fullscreen" title="Tela inteira">fullscreen</i>
                    </span>
                </div>
            </div>
        </div>
        <div id="settings">
            <div class="playback">
                <span>Velocidade da Reprodução</span>
                <ul>
                    <li data-speed="0.25">0.25</li>
                    <li data-speed="0.5">0.5</li>
                    <li data-speed="0.75">0.75</li>
                    <li data-speed="1" class="active">Normal</li>
                    <li data-speed="1.25">1.25</li>
                    <li data-speed="1.5">1.5</li>
                    <li data-speed="1.75">1.75</li>
                    <li data-speed="2">2</li>
                </ul>
            </div>
        </div>
    </div>
</div>

My JavaScript for the video player:

// Select elements
const vidsrc = document.querySelector('#main-video').src;
const video_player = document.querySelector('#video_player'),
mainVideo = video_player.querySelector('#main-video'),
progressAreaTime = video_player.querySelector('.progressAreaTime'),
controls = video_player.querySelector('.controls'),
progressArea = video_player.querySelector('.progress-area'),
progress_Bar = video_player.querySelector('.progress-bar'),
buffered_Bar = video_player.querySelector('.buffered-progress-bar'),
fast_rewind = video_player.querySelector('.fast-rewind'),
play_pause = video_player.querySelector('.play_pause'),
fast_forward = video_player.querySelector('.fast-forward'),
volume = video_player.querySelector('.volume'),
volume_range = video_player.querySelector('.volume_range'),
current = video_player.querySelector('.current'),
totalDuration = video_player.querySelector('.duration'),
auto_play = video_player.querySelector('.auto-play'),
settingsBtn = video_player.querySelector('.settingsBtn'),
picture_in_picture = video_player.querySelector('.picture_in_picture'),
fullscreen = video_player.querySelector('.fullscreen'),
settings = video_player.querySelector('#settings'),
playback = video_player.querySelectorAll('.playback li');

mainVideo.addEventListener('loadeddata',()=>{
    setInterval(() => {
        let bufferedTime = mainVideo.buffered.end(0);
        let duration = mainVideo.duration;
        let width = (bufferedTime / duration) * 100;
        buffered_Bar.style.width = `${width}%`
    }, 500);
})

// Play
function playVideo() {
    play_pause.innerHTML = "pause";
    play_pause.title = "Pausar";
    video_player.classList.add('paused')
    mainVideo.play();
}

// Pause
function pauseVideo() {
    play_pause.innerHTML = "play_arrow";
    play_pause.title = "Reproduzir";
    video_player.classList.remove('paused')
    mainVideo.pause();
}

play_pause.addEventListener('click',()=>{
    const isVideoPaused = video_player.classList.contains('paused');
    isVideoPaused ? pauseVideo() : playVideo();
})

mainVideo.addEventListener('play',()=>{
    playVideo();
})

mainVideo.addEventListener('pause',()=>{
    pauseVideo();
})

// Rewind
fast_rewind.addEventListener('click',()=>{
    mainVideo.currentTime -= 10;
})

// Forward
fast_forward.addEventListener('click',()=>{
    mainVideo.currentTime += 10;
})

// Total duration
mainVideo.addEventListener("loadeddata",(e)=>{
    let videoDuration = e.target.duration;
    let totalMin = Math.floor(videoDuration / 60);
    let totalSec = Math.floor(videoDuration % 60);

    // Complete with zero at beggining
    totalSec < 10 ? totalSec = "0"+totalSec:totalSec;
    totalMin < 10 ? totalMin = "0"+totalMin:totalMin;
    totalDuration.innerHTML = `${totalMin}:${totalSec}`;
})

// Current duration
mainVideo.addEventListener('timeupdate',(e)=>{
    let currentVideoTime = e.target.currentTime;
    let currentMin = Math.floor(currentVideoTime / 60);
    let currentSec = Math.floor(currentVideoTime % 60);
   // Complete with zero at beggining
    currentSec < 10 ? currentSec = "0"+currentSec:currentSec; 
    currentMin < 10 ? currentMin = "0"+currentMin:currentMin; 
    current.innerHTML = `${currentMin}:${currentSec}`;

    let videoDuration = e.target.duration
    // Seek bar
    let progressWidth = (currentVideoTime / videoDuration) * 100;
    progress_Bar.style.width = `${progressWidth}%`;
})

// Update current duration
progressArea.addEventListener('click',(e)=>{
    let videoDuration = mainVideo.duration;
    let progressWidthval = progressArea.clientWidth;
    let ClickOffsetX = e.offsetX;
    mainVideo.currentTime = (ClickOffsetX / progressWidthval) * videoDuration;
})

// Volume
function changeVolume() {
    mainVideo.volume = volume_range.value / 100;
    if (volume_range.value == 0) {
        volume.innerHTML = "volume_off";
    }else if(volume_range.value < 40){
        volume.innerHTML = "volume_down";
    }else{
        volume.innerHTML = "volume_up";
    }
}

function muteVolume() {
    if (volume_range.value == 0) {
        volume_range.value = 80;
        mainVideo.volume = 0.8;
        volume.innerHTML = "volume_up";
        volume.title = "Sem áudio";
    }else{
        volume_range.value = 0;
        mainVideo.volume = 0;
        volume.innerHTML = "volume_off";
        volume.title = "Reativar o som";
    }
}

volume_range.addEventListener('change',()=>{
    changeVolume();
})

volume.addEventListener('click',()=>{
    muteVolume();
})

// Update on mouse move
progressArea.addEventListener('mousemove',(e)=>{
    let progressWidthval = progressArea.clientWidth;
    let x = e.offsetX;
    progressAreaTime.style.setProperty('--x',`${x}px`);
    progressAreaTime.style.display = "block";
    let videoDuration = mainVideo.duration;
    let progressTime = Math.floor((x/progressWidthval)*videoDuration);
    let currentMin = Math.floor(progressTime / 60);
    let currentSec = Math.floor(progressTime % 60);
   // Complete with zero at beggining
    currentSec < 10 ? currentSec = "0"+currentSec:currentSec; 
    currentMin < 10 ? currentMin = "0"+currentMin:currentMin; 
    progressAreaTime.innerHTML = `${currentMin}:${currentSec}`;
})

progressArea.addEventListener('mouseleave',()=>{
    progressAreaTime.style.display = "none";
})

// Loop
auto_play.addEventListener('click',()=>{
    auto_play.classList.toggle('active')
    if(auto_play.classList.contains('active')){
        auto_play.title = "A repetição automática está ativada";
    }else{
        auto_play.title = "A repetição automática está desativada";
    }
});

mainVideo.addEventListener("ended",()=>{
    if (auto_play.classList.contains('active')) {
        playVideo();
    }else{
        play_pause.innerHTML = "replay";
        play_pause.title = "Reproduzir novamente";
    }
});

// Picture in picture
picture_in_picture.addEventListener('click',()=>{
    mainVideo.requestPictureInPicture();
})

// Full screen
fullscreen.addEventListener('click',()=>{
    if (!video_player.classList.contains('openFullScreen')) {
        video_player.classList.add('openFullScreen');
        fullscreen.innerHTML = "fullscreen_exit";
        fullscreen.title = "Sair da tela inteira";
        video_player.requestFullscreen();
    }else{
        video_player.classList.remove('openFullScreen');
        fullscreen.innerHTML = "fullscreen";
        fullscreen.title = "Tela inteira";
        document.exitFullscreen();
    }
});

// Settings
settingsBtn.addEventListener('click',()=>{
    settings.classList.toggle('active');
    settingsBtn.classList.toggle('active');
})

// Speed
playback.forEach((event)=>{
    event.addEventListener('click',()=>{
        removeActiveClasses();
        event.classList.add('active');
        let speed = event.getAttribute('data-speed');
        mainVideo.playbackRate = speed;
    })
})

function removeActiveClasses() {
    playback.forEach(event => {
        event.classList.remove('active')
    });
}

// Get URL
let xhr = new XMLHttpRequest();
xhr.open("GET",vidsrc);
xhr.responseType = "arraybuffer";
xhr.onload = (e)=>{
    let blob = new Blob([xhr.response]);
    let url = URL.createObjectURL(blob);
    mainVideo.src = url;
}
xhr.send();

// Store duration
window.addEventListener('unload',()=>{
    let setDuration = localStorage.setItem('duration',`${mainVideo.currentTime}`);
    let setSrc = localStorage.setItem('src',`${mainVideo.getAttribute('src')}`);
})

window.addEventListener('load',()=>{
    let getDuration = localStorage.getItem('duration');
    let getSrc = localStorage.getItem('src');
    if (getSrc) {
        mainVideo.src = getSrc;
        mainVideo.currentTime = getDuration;
    }
})

mainVideo.addEventListener('contextmenu',(e)=>{
    e.preventDefault();
})

// Hide and show controls (mouse)
video_player.addEventListener('mouseover',()=>{
    controls.classList.add('active');
})

video_player.addEventListener('mouseleave',()=>{
    if (video_player.classList.contains('paused')) {
        if (settingsBtn.classList.contains('active')) {
            controls.classList.add('active');
        }else{
            controls.classList.remove('active')
        }
    }else{
        controls.classList.add('active')
    }
})

if (video_player.classList.contains('paused')) {
    if (settingsBtn.classList.contains('active')) {
        controls.classList.add('active');
    }else{
        controls.classList.remove('active')
    }
}else{
    controls.classList.add('active')
}

// Hide and show controls (mobile)
video_player.addEventListener('touchstart',()=>{
    controls.classList.add('active');
    setTimeout(() => {
        controls.classList.remove('active')
    }, 8000);
})

video_player.addEventListener('touchmove',()=>{
    if (video_player.classList.contains('paused')) {
        controls.classList.remove('active')
    }else{
        controls.classList.add('active')
    }
})
8
  • You really just need to make this part dynamic: src="./media/portfolio/videos/Show-Reel-2021.mp4" ... Commented Apr 4, 2022 at 20:59
  • @Asleepace The source you mentioned is dynamic, I copy and paste the html code for every video instance on the page. However, the javascript part does not work for more than one video Commented Apr 5, 2022 at 3:57
  • what you should do is have a JS function that replaces the HTML with each instance of file name you have for src. I can write something up for you, but it’s prob better for your portfolio to reason it out. Commented Apr 5, 2022 at 3:59
  • @Asleepace My portfolio is not about coding. I work with photos and videos :) You don't really have to write it for me, but I would be glad if you could give a few tips about how that function works Commented Apr 5, 2022 at 4:08
  • ah ok no worries, I’m on my phone atm so I’ll write up something more detailed tomorrow, the general idea is you only need one JS function which can replace the src with a variable. JS can manipulate the HTML dom (page) so if you had an array of file names (aka where the videos are uploaded) then it will be really simple. Commented Apr 5, 2022 at 4:13

2 Answers 2

2

Helper function which you can use to write the HTML skeleton once and then have it dynamically created for multiple instances

function renderVideoPlayers() {

  const sources = [
    '../my-video-src-1',
    '../my-video-src-2',
    '../my-video-src-3',
    '../my-video-src-4',
  ]

  const videoPlayers = sources.map((video) => `
  <section class="videoplayer">
  <div class="c-video">
    <div id="video_player">
        <video src="${video}" class="video"></video>
        <div class="progressAreaTime">00:00</div>
        <div class="controls">
            <div class="progress-area">
                <div class="progress-bar">
                    <span></span>
                </div>
                <div class="buffered-progress-bar"></div>
            </div>
            <div class="controls-list">
                <div class="controls-left">
                    <span class="icon">
                        <i class="material-icons fast-rewind" title="Retroceder 10 segundos">first_page</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons play_pause" title="Reproduzir">play_arrow</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons fast-forward" title="Avançar 10 segundos">last_page</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons volume" title="Sem áudio">volume_up</i>
                        <input type="range" min="0" max="100" class="volume_range">
                    </span>
                    <div class="timer">
                        <span class="current">00:00</span> / <span class="duration">0:00</span>
                    </div>
                </div>
                <div class="controls-right">
                    <span class="icon">
                        <i class="material-icons auto-play" title="A repetição automática está desativada"></i>
                    </span>
                    <span class="icon">
                        <i class="material-icons settingsBtn" title="Detalhes">settings</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons picture_in_picture" title="Miniplayer">picture_in_picture_alt</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons fullscreen" title="Tela inteira">fullscreen</i>
                    </span>
                </div>
            </div>
        </div>
        <div id="settings">
            <div class="playback">
                <span>Velocidade da Reprodução</span>
                <ul>
                    <li data-speed="0.25">0.25</li>
                    <li data-speed="0.5">0.5</li>
                    <li data-speed="0.75">0.75</li>
                    <li data-speed="1" class="active">Normal</li>
                    <li data-speed="1.25">1.25</li>
                    <li data-speed="1.5">1.5</li>
                    <li data-speed="1.75">1.75</li>
                    <li data-speed="2">2</li>
                </ul>
            </div>
        </div>
    </div>
</div>
`)

  // get an element already on the page add the rendered
  // html for each video into that section.
  const container = document.getElementById('container')
  container.innerHTML = videoPlayers.join("")
}

// call this function when you want it to be displayed
renderVideoPlayers()
<html>
<head>
</head>
<body>
<div id="container">
</div>
</body>
</html>

You will also need to do the same with Javascript as well as ensure that there aren't multiple instance of the same id. This shouldn't be too hard as you can switch similiar styles to class and make the id's something like video-1, video-2, video-3, etc.

A couple notes

  • sources: this is an array of all video urls you wish to display on the page.
  • sources.map: will iterate through the array and interpolate a string with the video player HTML and video source
  • videoPlayers: is now an array of HTML markup
  • container: will be a parent element on the page where you wish to append all the HTML markup
Sign up to request clarification or add additional context in comments.

4 Comments

Now I understand how it is supposed to work, but I have some questions. Should I load the javascript inside the current html function, should it have it's own function or should it be loaded from another external .js file? And what is the best way to load the css along the code? I tried loading it before, after the html code and within the function using .createElement/.appendChild, but none worked. Does it need to be embed into the html?
@lucasrvimieiro I would replace all styles that rely on id="..." with class styles class="..." so they can be shared. The id properties will need to be different and each have the corresponding logic for playback. Without seeing the actual page it's a bit hard to say what's the best way. Ideally something like the React framework would make this a lot simpler.
Thanks a lot, I'm a few months new to js and html. I will be trying to work it out using your function, but for now your help has been the best one out there for me and it will definitely help a lot of people out there!
@lucasrvimieiro no worries glad to help, if you get stuck just message and I'll try to help as best I can, nice work so far!
0

I HAVE FOUND THE SOLUTION

For anyone in the future seeking for the same solution, I was able to make it work by first selecting every videoplayer div:

const videoContainers = document.querySelectorAll(".video-container")

Then using the .forEach() function so that the code will generate a button for every individual videoplayer:

videoContainers.forEach((container) => {
  let playPauseBtn = container.querySelector(".play-pause-btn");
  let theaterBtn = container.querySelector(".theater-btn");
  let fullScreenBtn = container.querySelector(".full-screen-btn");
...

// The entire code for one videoplayer
}

You can reference "container" for the individual videoplayer or "document" for every videoplayer at once. It just works like magic!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.