Podcastspeler met hoofdstukken New
Audio player for a podcast or recorded webinar: play, back 15 seconds, forward 30, speed (1x to 2x), a timeline with ticks per chapter and a chapter list that follows along and lets you jump. A plain audio element underneath, so the phone media keys work (Media Session with title and chapter). The demo plays a calm tone. For practices, advisers and associations with their own podcast.
0:00
How to use it
- Paste the HTML where the components should appear.
- Add the CSS to your stylesheet. All classes are prefixed, so nothing clashes with your own styles.
- Add the JavaScript and call init() with the element that contains the HTML.
<!-- Podcastspeler met hoofdstukken · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="pch">
<audio class="pch__a" preload="metadata" src="https://neddev.com/lab-media/podcast-hoofdstukken-audio.mp3"></audio>
<div class="pch__top">
<img class="pch__cov" src="https://neddev.com/lab-media/podcast-hoofdstukken-1.webp" alt="" width="600" height="327">
<div class="pch__meta"><b class="pch__ti">Personeel vinden in de bouw</b><span class="pch__sub">Ondernemen in Zwolle, aflevering 14</span></div>
</div>
<div class="pch__ctl">
<button class="pch__b pch__back" type="button" aria-label="15 seconden terug"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12a7 7 0 1 0 2.1-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><path d="M4 3.5v4.4h4.4" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg><span>15</span></button>
<button class="pch__b pch__play" type="button" aria-label="Afspelen"><svg class="pch__i-pl" viewBox="0 0 24 24" aria-hidden="true"><path d="M8 5.5v13l10.5-6.5z"/></svg><svg class="pch__i-pa" viewBox="0 0 24 24" aria-hidden="true"><path d="M7 5h3.5v14H7zM13.5 5H17v14h-3.5z"/></svg></button>
<button class="pch__b pch__fwd" type="button" aria-label="30 seconden vooruit"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M19 12a7 7 0 1 1-2.1-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><path d="M20 3.5v4.4h-4.4" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg><span>30</span></button>
<div class="pch__tlw"><div class="pch__ticks" aria-hidden="true"></div><input class="pch__tl" type="range" min="0" max="160" step="1" value="0" aria-label="Tijdlijn" aria-valuetext="0:00 van 2:40"></div>
<span class="pch__t">0:00</span>
<button class="pch__spd" type="button" aria-label="Afspeelsnelheid 1 keer">1x</button>
</div>
<ol class="pch__ch" aria-label="Hoofdstukken"></ol>
</div>
</div>/* Podcastspeler met hoofdstukken · NedDev · eigen werk. */
.pch{position:absolute;inset:0;display:flex;flex-direction:column;padding:12px 14px 8px;background:#fff;color:#0a0a0a;font-family:'Switzer',sans-serif;text-align:left;box-sizing:border-box}
.pch__top{display:flex;align-items:center;gap:10px}
.pch__cov{flex:none;width:44px;height:44px;border-radius:4px;object-fit:cover}
.pch__meta{display:flex;flex-direction:column;gap:2px;min-width:0}
.pch__ti{font:600 15px/1.2 'Switzer',sans-serif;letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pch__sub{font:400 12px/1.3 'Switzer',sans-serif;color:#5c5a57}
.pch__ctl{display:flex;align-items:center;gap:4px;margin:8px 0 6px}
.pch__b{position:relative;flex:none;width:30px;height:30px;padding:0;border:0;border-radius:4px;background:transparent;color:#0a0a0a;display:grid;place-items:center;cursor:pointer}
.pch__b svg{width:22px;height:22px;fill:currentColor}
.pch__back svg,.pch__fwd svg{fill:none}
.pch__b span{position:absolute;left:0;right:0;top:50%;margin-top:-6px;font:600 12px/12px 'Switzer',sans-serif;text-align:center;transform:scale(.72)}
.pch__b:hover{background:#f4f3f1}
.pch__play{width:34px;height:34px;background:#F5480F;color:#fff}
.pch__play:hover{background:#e03f0a}
.pch__i-pa{display:none}.pch.is-playing .pch__i-pl{display:none}.pch.is-playing .pch__i-pa{display:block}
.pch__tlw{position:relative;flex:1 1 auto;min-width:40px;height:24px}
.pch__ticks{position:absolute;left:6px;right:6px;top:50%;height:10px;margin-top:-5px;pointer-events:none}
.pch__ticks i{position:absolute;top:0;width:2px;height:10px;margin-left:-1px;background:#0a0a0a}
.pch__tl{position:relative;width:100%;height:24px;margin:0;-webkit-appearance:none;appearance:none;background:transparent;cursor:pointer;--p:0%}
.pch__tl::-webkit-slider-runnable-track{height:4px;border-radius:2px;background:linear-gradient(to right,#F5480F var(--p),#e1ded9 var(--p))}
.pch__tl::-moz-range-track{height:4px;border-radius:2px;background:linear-gradient(to right,#F5480F var(--p),#e1ded9 var(--p))}
.pch__tl::-webkit-slider-thumb{-webkit-appearance:none;width:12px;height:12px;margin-top:-4px;border-radius:50%;background:#0a0a0a;border:0}
.pch__tl::-moz-range-thumb{width:12px;height:12px;border-radius:50%;background:#0a0a0a;border:0}
.pch__t{flex:none;width:34px;font:500 12px/1 'Switzer',sans-serif;font-variant-numeric:tabular-nums;text-align:right}
.pch__spd{flex:none;min-width:36px;height:26px;padding:0 6px;border:1.5px solid #0a0a0a;border-radius:4px;background:#fff;color:#0a0a0a;font:600 12px/1 'Switzer',sans-serif;cursor:pointer;font-variant-numeric:tabular-nums}
.pch__b:focus-visible,.pch__spd:focus-visible,.pch__tl:focus-visible,.pch__cb:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.pch__ch{list-style:none;margin:0 -6px;padding:0 0 10px;overflow-y:auto;-webkit-mask:linear-gradient(#000 calc(100% - 18px),transparent);mask:linear-gradient(#000 calc(100% - 18px),transparent);flex:1 1 auto;min-height:0;scrollbar-width:thin}
.pch__cb{display:flex;gap:10px;width:100%;min-height:26px;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:#0a0a0a;font:400 13px/1.3 'Switzer',sans-serif;text-align:left;cursor:pointer}
.pch__cb time{flex:none;width:30px;color:#5c5a57;font-variant-numeric:tabular-nums;font-size:12px;line-height:17px}
.pch__cb:hover{background:#f4f3f1}
.pch__cb[aria-current="true"]{background:#f4f3f1;font-weight:600}
.pch__cb[aria-current="true"] time{color:#F5480F}// Podcastspeler met hoofdstukken · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.pch');if(!w)return;
var a=w.querySelector('.pch__a'),play=w.querySelector('.pch__play'),tl=w.querySelector('.pch__tl'),t=w.querySelector('.pch__t'),spd=w.querySelector('.pch__spd'),ol=w.querySelector('.pch__ch'),ticks=w.querySelector('.pch__ticks');
var C=[[0,'Even voorstellen: Sanne de Wit van Bouwbedrijf Hattem'],[24,'Waarom vakmensen bij een kleine aannemer kiezen'],[62,'Een vacaturetekst die wel werkt'],[101,'Stageplekken via het Deltion College'],[133,'Vragen van luisteraars']];
var D=160,rates=[1,1.25,1.5,2],ri=0,cur=-1;
function f(s){s=Math.max(0,Math.floor(s||0));return Math.floor(s/60)+':'+String(s%60).padStart(2,'0');}
var btns=C.map(function(c){var li=document.createElement('li'),b=document.createElement('button');b.type='button';b.className='pch__cb';b.innerHTML='<time>'+f(c[0])+'</time><span></span>';b.querySelector('span').textContent=c[1];
b.setAttribute('aria-label',c[1]+', begint op '+f(c[0]));b.addEventListener('click',function(){a.currentTime=c[0];if(a.paused)toggle();});li.appendChild(b);ol.appendChild(li);return b;});
function drawTicks(){ticks.innerHTML='';C.slice(1).forEach(function(c){var i=document.createElement('i');i.style.left=(c[0]/D*100)+'%';ticks.appendChild(i);});}
function ch(s){var k=0;C.forEach(function(c,i){if(s>=c[0])k=i;});return k;}
function upd(){var s=a.currentTime;tl.value=s;tl.style.setProperty('--p',(s/D*100).toFixed(2)+'%');t.textContent=f(s);tl.setAttribute('aria-valuetext',f(s)+' van '+f(D)+', '+C[ch(s)][1]);
var k=ch(s);if(k!==cur){cur=k;btns.forEach(function(b,i){if(i===k)b.setAttribute('aria-current','true');else b.removeAttribute('aria-current');});
var li=btns[k].parentNode;if(li.offsetTop<ol.scrollTop||li.offsetTop+li.offsetHeight>ol.scrollTop+ol.clientHeight)ol.scrollTop=li.offsetTop-ol.offsetTop;meta();}}
function meta(){if(!('mediaSession' in navigator))return;try{navigator.mediaSession.metadata=new MediaMetadata({title:'Personeel vinden in de bouw · '+C[Math.max(0,cur)][1],artist:'Ondernemen in Zwolle',album:'Aflevering 14'});}catch(_){}}
function toggle(){if(a.paused){var p=a.play();if(p&&p.catch)p.catch(function(){});}else a.pause();}
function sync(){var p=!a.paused;w.classList.toggle('is-playing',p);play.setAttribute('aria-label',p?'Pauzeren':'Afspelen');}
play.addEventListener('click',toggle);a.addEventListener('play',sync);a.addEventListener('pause',sync);a.addEventListener('ended',sync);
a.addEventListener('timeupdate',upd);a.addEventListener('loadedmetadata',function(){if(isFinite(a.duration)){D=a.duration;tl.max=Math.floor(D);drawTicks();upd();}});
tl.addEventListener('input',function(){a.currentTime=+tl.value;upd();});
w.querySelector('.pch__back').addEventListener('click',function(){a.currentTime=Math.max(0,a.currentTime-15);upd();});
w.querySelector('.pch__fwd').addEventListener('click',function(){a.currentTime=Math.min(D,a.currentTime+30);upd();});
spd.addEventListener('click',function(){ri=(ri+1)%rates.length;a.playbackRate=rates[ri];var l=String(rates[ri]).replace('.',',');spd.textContent=l+'x';spd.setAttribute('aria-label','Afspeelsnelheid '+l+' keer');});
if('mediaSession' in navigator){try{navigator.mediaSession.setActionHandler('seekbackward',function(){a.currentTime=Math.max(0,a.currentTime-15);});navigator.mediaSession.setActionHandler('seekforward',function(){a.currentTime=Math.min(D,a.currentTime+30);});
navigator.mediaSession.setActionHandler('previoustrack',function(){var k=ch(a.currentTime);a.currentTime=C[a.currentTime-C[k][0]>3?k:Math.max(0,k-1)][0];});navigator.mediaSession.setActionHandler('nexttrack',function(){var k=ch(a.currentTime);if(k<C.length-1)a.currentTime=C[k+1][0];});}catch(_){}}
drawTicks();upd();
}
init(document.querySelector('.jouw-container'));
More components
Seen something you like?
Add it to your selection and send it over. We build your site around it, in your brand.
Get in touch →