To make video popup on Portfolio item click, like this.
You can also use this tool to add Portfolio, so you can add Video Popup easier without using complex code.
#1. First, find Portfolio item url
In my example, we will have:
/projects/apple
/projects/google
/projects/microsoft
#2. Use this code to Code Injection > Footer (or Page Header Injection)
<div id="videoLightbox" class="lightbox">
<div class="lightbox-content">
<button class="close-btn" id="closeBtn">×</button>
<div class="loading" id="loading">Loading....</div>
<div class="video-container" id="videoContainer">
<iframe id="videoPlayer" class="video-player" frameborder="0" allow="autoplay; fullscreen; picture-in-picture"></iframe>
</div>
<div class="video-controls" id="videoControls">
<div class="progress-container" id="progressContainer">
<div class="progress-bar" id="progressBar"></div>
</div>
<div class="controls-bottom">
<span class="play-pause-text" id="playPauseBtn">Play</span>
<span class="fullscreen-text" id="fullscreenBtn">Fullscreen</span>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const lightbox = document.getElementById('videoLightbox');
const videoContainer = document.getElementById('videoContainer');
const closeBtn = document.getElementById('closeBtn');
const loading = document.getElementById('loading');
const playPauseBtn = document.getElementById('playPauseBtn');
const progressContainer = document.getElementById('progressContainer');
const progressBar = document.getElementById('progressBar');
const fullscreenBtn = document.getElementById('fullscreenBtn');
const videoControls = document.getElementById('videoControls');
let player = null;
let isPlaying = false;
let duration = 0;
let currentTime = 0;
function getVimeoId(url) {
const match = url.match(/vimeo\.com\/(\d+)/);
return match ? match[1] : null;
}
function cleanupPlayer() {
if (player) {
try {
player.destroy();
} catch(e) {
console.log('Error destroying player:', e);
}
player = null;
}
const existingIframe = document.getElementById('videoPlayer');
if (existingIframe) {
existingIframe.remove();
}
}
function createNewIframe() {
const newIframe = document.createElement('iframe');
newIframe.id = 'videoPlayer';
newIframe.className = 'video-player';
newIframe.setAttribute('frameborder', '0');
newIframe.setAttribute('allow', 'autoplay; fullscreen; picture-in-picture');
videoContainer.appendChild(newIframe);
return newIframe;
}
function openLightbox(videoUrl) {
const vimeoId = getVimeoId(videoUrl);
if (!vimeoId) return;
cleanupPlayer();
lightbox.classList.add('active');
loading.style.display = 'block';
videoControls.style.display = 'none';
setTimeout(() => {
const iframe = createNewIframe();
iframe.src = `https://player.vimeo.com/video/${vimeoId}?api=1&autoplay=1&controls=0`;
if (window.Vimeo) {
setTimeout(() => {
initializeVimeoPlayer(iframe);
}, 500);
} else {
const script = document.createElement('script');
script.src = 'https://player.vimeo.com/api/player.js';
script.onload = function() {
setTimeout(() => {
initializeVimeoPlayer(iframe);
}, 500);
};
document.head.appendChild(script);
}
}, 100);
}
function initializeVimeoPlayer(iframe) {
try {
player = new Vimeo.Player(iframe);
player.ready().then(function() {
loading.style.display = 'none';
videoControls.style.display = 'block';
player.play().catch(function(error) {
console.log('Autoplay failed:', error);
});
return player.getDuration();
}).then(function(dur) {
duration = dur;
}).catch(function(error) {
console.log('Player error:', error);
loading.style.display = 'none';
videoControls.style.display = 'block';
});
player.on('play', function() {
isPlaying = true;
playPauseBtn.textContent = 'Pause';
});
player.on('pause', function() {
isPlaying = false;
playPauseBtn.textContent = 'Play';
});
player.on('timeupdate', function(data) {
currentTime = data.seconds;
const progress = (currentTime / duration) * 100;
progressBar.style.width = `${progress}%`;
});
} catch(error) {
console.log('Error initializing player:', error);
loading.style.display = 'none';
}
}
function closeLightbox() {
lightbox.classList.remove('active');
setTimeout(() => {
cleanupPlayer();
isPlaying = false;
duration = 0;
currentTime = 0;
progressBar.style.width = '0%';
playPauseBtn.textContent = 'Play';
}, 300);
}
const linkMapping = {
'netflix': {
from: '/projects/apple',
to: 'https://vimeo.com/1006416923'
},
'nokia': {
from: '/projects/google',
to: 'https://vimeo.com/1077793525'
},
'openai': {
from: '/projects/microsoft',
to: 'https://vimeo.com/810778889'
}
};
function setupVideoLinks() {
Object.values(linkMapping).forEach(mapping => {
const links = document.querySelectorAll(`a[href="${mapping.from}"]`);
links.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
openLightbox(mapping.to);
});
});
});
document.querySelectorAll('a[href*="vimeo.com"]').forEach(item => {
item.addEventListener('click', function(e) {
e.preventDefault();
const videoUrl = this.getAttribute('href');
openLightbox(videoUrl);
});
});
}
setupVideoLinks();
closeBtn.addEventListener('click', closeLightbox);
lightbox.addEventListener('click', function(e) {
if (e.target === lightbox) {
closeLightbox();
}
});
document.addEventListener('keydown', function(e) {
if (lightbox.classList.contains('active')) {
if (e.key === 'Escape') {
closeLightbox();
} else if (e.key === ' ' && player) {
e.preventDefault();
if (isPlaying) {
player.pause();
} else {
player.play();
}
}
}
});
playPauseBtn.addEventListener('click', function() {
if (!player) return;
if (isPlaying) {
player.pause();
} else {
player.play();
}
});
progressContainer.addEventListener('click', function(e) {
if (!player) return;
const rect = progressContainer.getBoundingClientRect();
const clickX = e.clientX - rect.left;
const width = rect.width;
const percentage = clickX / width;
const seekTime = percentage * duration;
player.setCurrentTime(seekTime);
});
fullscreenBtn.addEventListener('click', function() {
if (!player) return;
player.requestFullscreen();
});
});
</script>
Remember to update Portfolio item url + Video URL
#3. Use this code to Custom CSS
/* Portfolio video popup */
.lightbox {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.9);
z-index: 9999;
opacity: 0;
transition: opacity 0.3s ease;
}
.lightbox.active {
display: flex;
opacity: 1;
align-items: center;
justify-content: center;
}
.lightbox-content {
position: relative;
width: 90%;
max-width: 900px;
background: #000;
border-radius: 10px;
overflow: hidden;
transform: scale(0.8);
transition: transform 0.3s ease;
}
.lightbox.active .lightbox-content {
transform: scale(1);
}
.video-container {
position: relative;
width: 100%;
height: 0;
padding-bottom: 56.25%;
}
.video-player {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.video-controls {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(transparent, rgba(0,0,0,0.8));
padding: 20px 15px 15px;
opacity: 0;
transition: opacity 0.3s ease;
}
.lightbox-content:hover .video-controls {
opacity: 1;
}
.progress-container {
width: 100%;
height: 2px;
background: hsla(0,0%,100%,.2);
border-radius: 3px;
cursor: pointer;
margin-bottom: 15px;
}
.progress-bar {
height: 100%;
background: #fff;
border-radius: 3px;
width: 0%;
transition: width 0.1s ease;
}
.controls-bottom {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.play-pause-text {
color: white;
font-size: 14px;
cursor: pointer;
transition: opacity 0.2s ease;
}
.play-pause-text:hover {
opacity: 0.8;
}
.fullscreen-text {
color: white;
font-size: 14px;
cursor: pointer;
transition: opacity 0.2s ease;
}
.fullscreen-text:hover {
opacity: 0.8;
}
.close-btn {
position: absolute;
top: 15px;
right: 15px;
background: rgba(0,0,0,0.6);
color: white;
border: none;
width: 40px;
height: 40px;
border-radius: 50%;
cursor: pointer;
font-size: 20px;
z-index: 10;
transition: background 0.2s ease;
}
.close-btn:hover {
background: rgba(0,0,0,0.8);
}
.loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-size: 18px;
}
@media (max-width: 768px) {
.lightbox-content {
width: 95%;
margin: 20px;
}
.video-controls {
padding: 15px 10px 10px;
}
}