I'm using the following HTML, CSS and Javascript to disguise a video player as an audio player.
It works perfectly in Firefox but in Chrome pressing the play button does not actually play anything.
What is preventing it from working in chrome and how can I get it to work in Chrome too?
Update:
I want to expand on a observation that I think maybe helpful (A special thank you to @AHaworth for making a similar observation in the comments):
If I remove iframe { display: none; }
& the video player initially loads alongside the audio player, the audio player controls (still) has NO effect on the video player. However, if I click play on the video player and then hide it completely using CSS I am now able to fully control the video player through the audio player.. even though the video player is completely hidden.
This observation is similar to @AHaworth in the comments "The error in the console that seems most relevant is the one saying that can't play the video because the user has not interacted."
So the right approach is to force "interactivity" but how?
Check out the CodePen here.
let player;
const playBtn = document.getElementById('play');
const rewindBtn = document.getElementById('rewind');
const forwardBtn = document.getElementById('forward');
const progressBar = document.getElementById('progress-bar');
const currentTimeEl = document.getElementById('current-time');
const endTimeEl = document.getElementById('end-time');
const speedControl = document.getElementById('speed-control');
const volumeBtn = document.getElementById('volume-btn');
const volumeSlider = document.getElementById('volume-slider');
let duration = 0;
window.addEventListener('load', function() {
player = new playerjs.Player(document.getElementById("bunny-stream-embed"));
player.on('ready', () => {
console.log('Player is ready');
player.getDuration(d => {
duration = d;
endTimeEl.innerText = formatTime(duration);
progressBar.max = duration;
});
// Set initial volume to 70%
player.setVolume(70);
volumeSlider.value = 70;
updateVolumeIcon(0.7);
// Set up event listeners after player is ready
setupEventListeners();
});
player.on('timeupdate', (data) => {
currentTimeEl.innerText = formatTime(data.seconds);
progressBar.value = data.seconds;
});
player.on('play', () => {
playBtn.innerHTML = '❚❚'; // Pause icon
});
player.on('pause', () => {
playBtn.innerHTML = '►'; // Play icon
});
});
function setupEventListeners() {
playBtn.addEventListener('click', () => {
player.getPaused(paused => {
if (paused) {
player.play();
} else {
player.pause();
}
});
});
rewindBtn.addEventListener('click', () => {
player.getCurrentTime(currentTime => {
player.setCurrentTime(Math.max(0, currentTime - 15));
});
});
forwardBtn.addEventListener('click', () => {
player.getCurrentTime(currentTime => {
player.setCurrentTime(Math.min(duration, currentTime + 15));
});
});
progressBar.addEventListener('input', (e) => {
const time = parseFloat(e.target.value);
player.setCurrentTime(time);
});
volumeSlider.addEventListener('input', (e) => {
const volume = e.target.value;
player.setVolume(volume);
updateVolumeIcon(volume);
});
speedControl.addEventListener('click', () => {
const speedOptions = [1, 1.25, 1.5, 2];
let currentSpeed = parseFloat(speedControl.innerText.replace('x', ''));
const newSpeed = speedOptions[(speedOptions.indexOf(currentSpeed) + 1) % speedOptions.length];
speedControl.innerText = `${newSpeed}x`;
player.setPlaybackRate(newSpeed);
});
volumeBtn.addEventListener('click', () => {
player.getVolume(volume => {
if (volume > 0) {
player.setVolume(0);
volumeSlider.value = 0;
} else {
player.setVolume(70);
volumeSlider.value = 70;
}
updateVolumeIcon(volume > 0 ? 0 : 0.7);
});
});
}
function updateVolumeIcon(volume) {
if (volume > 0.5) {
volumeBtn.innerHTML = '🔊';
} else if (volume > 0) {
volumeBtn.innerHTML = '🔉';
} else {
volumeBtn.innerHTML = '🔇';
}
}
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
}
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
iframe {
display: none;
}
.audio-player {
background-color: #1e3d59;
color: white;
width: 100%;
max-width: 800px;
border-radius: 10px;
overflow: hidden;
}
.player-top {
display: flex;
align-items: center;
padding: 20px;
}
.cover {
width: 60px;
height: 60px;
border-radius: 5px;
margin-right: 15px;
}
.info {
flex-grow: 1;
}
.title {
margin: 0;
font-size: 18px;
}
.author {
margin: 5px 0 0;
font-size: 14px;
opacity: 0.8;
}
.bookmark {
background: none;
border: none;
color: white;
font-size: 24px;
cursor: pointer;
}
.player-bottom {
background-color: #102c43;
padding: 15px 20px;
}
.progress {
display: flex;
align-items: center;
margin-bottom: 15px;
}
#progress-bar {
flex-grow: 1;
margin: 0 10px;
cursor: pointer;
}
.controls {
display: flex;
justify-content: space-between;
align-items: center;
}
.control-btn {
background: none;
border: none;
color: white;
font-size: 16px;
cursor: pointer;
}
.play-pause {
font-size: 24px;
}
.playback-rate,
.volume-control {
display: flex;
align-items: center;
}
#volume-slider {
width: 80px;
margin-left: 10px;
}
input[type="range"] {
-webkit-appearance: none;
background: transparent;
}
input[type="range"]::-webkit-slider-runnable-track {
width: 100%;
height: 4px;
background: #ffffff50;
border-radius: 2px;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 12px;
height: 12px;
background: white;
border-radius: 50%;
cursor: pointer;
margin-top: -4px;
}
<script type="text/javascript" src="https:////assets.mediadelivery.net/playerjs/player-0.1.0.min.js"></script>
<iframe id="bunny-stream-embed" src="https://iframe.mediadelivery.net/embed/197133/dc48a09e-d9bb-420a-83d7-72dc2304c034?autoplay=false&preload=true" width="720" height="400" frameborder="0" allow="autoplay"></iframe>
<div class="audio-player">
<div class="player-top">
<img class="cover" src="https://images.blinkist.io/images/books/5bf9dc9c6cee070007cab481/1_1/470.jpg" alt="Book Cover">
<div class="info">
<h2 class="title">Atomic Habits</h2>
<p class="author">James Clear</p>
</div>
<button class="bookmark">★</button>
</div>
<div class="player-bottom">
<div class="progress">
<span id="current-time">00:00</span>
<input id="progress-bar" type="range" min="0" max="100" value="0">
<span id="end-time">00:00</span>
</div>
<div class="controls">
<button id="rewind" class="control-btn">← 15</button>
<button id="play" class="control-btn play-pause">►</button>
<button id="forward" class="control-btn">15 →</button>
<div class="playback-rate">
<button id="speed-control" class="control-btn">1x</button>
</div>
<div class="volume-control">
<button id="volume-btn" class="control-btn">🔊</button>
<input type="range" id="volume-slider" min="0" max="100" value="70">
</div>
</div>
</div>
</div>
<script src="script.js"></script>
iframe { display: none; }
– Mischievousdisplay: none
with one of the "screenreader-friendly" ways of hiding content - css-tricks.com/inclusively-hidden, sitelint.com/blog/… – Combativeiframe { display: none; }
& the video player loads alongside the audio player, the audio player has NO effect on the video player. However, if I click play on the video player and then hide it using CSS I am now able to fully control the video player through the audio player.. So it seems the right approach is to force "interactivity" but how? – Mischievousiframe { display: none; }
– Sattlerplayer.js
hasn't been updated in 7 years and never went past v0.1; Is it the best choice of player? – Abba