Staafgrafiek met tabel Nieuw
Toegankelijke SVG-staafgrafiek (besparing per jaar) die zichzelf opbouwt uit een gewone HTML-tabel: staven groeien bij in beeld (IntersectionObserver), tooltip per staaf op hover en focus, pijltjestoetsen tussen staven en een knop naar de tabelweergave. Zonder JS of bij reduced motion blijft de tabel of een stilstaande grafiek over.
Zonnepanelen · fam. De Vries, Zwolle
Besparing per jaar
| Jaar | 2019 | 2020 | 2021 | 2022 | 2023 | 2024 | 2025 |
|---|---|---|---|---|---|---|---|
| Besparing | € 312 | € 498 | € 611 | € 702 | € 655 | € 840 | € 918 |
| t.o.v. vorig jaar | nvt | +60% | +23% | +15% | -7% | +28% | +9% |
Zo gebruik je het
- Plak de HTML op de plek waar het onderdeel moet komen.
- Zet de CSS in je stylesheet. Alle klassen hebben een eigen voorvoegsel, dus niets botst met je eigen opmaak.
- Voeg de JavaScript toe en roep init() aan met het element waar de HTML in staat.
<!-- Staafgrafiek met tabel · NedDev · eigen werk. -->
<div class="jouw-container">
<figure class="sbg"><div class="sbg__hd"><div><p class="sbg__k">Zonnepanelen · fam. De Vries, Zwolle</p><p class="sbg__t">Besparing per jaar</p></div><div class="sbg__sw" role="group" aria-label="Weergave"><button type="button" class="sbg__b" aria-pressed="true" data-v="g">Grafiek</button><button type="button" class="sbg__b" aria-pressed="false" data-v="t">Tabel</button></div></div>
<div class="sbg__plot"><div class="sbg__tip" hidden></div></div>
<table class="sbg__tbl"><caption class="sbg__sr">Besparing per jaar in euro's, zonnepanelen familie De Vries, Zwolle</caption><thead><tr><th scope="row">Jaar</th><th scope="col">2019</th><th scope="col">2020</th><th scope="col">2021</th><th scope="col">2022</th><th scope="col">2023</th><th scope="col">2024</th><th scope="col">2025</th></tr></thead><tbody><tr><th scope="row">Besparing</th><td data-v="312">€ 312</td><td data-v="498">€ 498</td><td data-v="611">€ 611</td><td data-v="702">€ 702</td><td data-v="655">€ 655</td><td data-v="840">€ 840</td><td data-v="918">€ 918</td></tr><tr><th scope="row">t.o.v. vorig jaar</th><td>nvt</td><td>+60%</td><td>+23%</td><td>+15%</td><td>-7%</td><td>+28%</td><td>+9%</td></tr></tbody></table>
<figcaption class="sbg__f">Bron: omvormer-data · stroomprijs € 0,28 per kWh</figcaption></figure>
</div>/* Staafgrafiek met tabel · NedDev · eigen werk. */
.sbg{position:absolute;inset:0;margin:0;box-sizing:border-box;padding:14px 16px 10px;background:#fff;color:#0a0a0a;font-family:'Switzer',system-ui,sans-serif;display:flex;flex-direction:column;gap:6px}
.sbg__hd{display:flex;justify-content:space-between;align-items:flex-start;gap:10px}
.sbg__k{margin:0;font:500 10px/1.3 'JetBrains Mono',monospace;letter-spacing:.04em;color:#5f5e5a}
.sbg__t{margin:2px 0 0;font-size:15px;font-weight:650;line-height:1.2}
.sbg__sw{display:flex;border:1px solid rgba(10,10,10,.18);border-radius:999px;padding:2px;flex:none}
.sbg__b{font:600 11px 'Switzer',system-ui,sans-serif;border:0;background:none;color:#5f5e5a;padding:4px 10px;border-radius:999px;cursor:pointer;transition:background .15s,color .15s}
.sbg__b:hover{color:#0a0a0a}
.sbg__b[aria-pressed="true"]{background:#0a0a0a;color:#fff}
.sbg__b:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.sbg__plot{position:relative;flex:1;min-height:0}
.sbg__svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.sbg__grid{stroke:rgba(10,10,10,.08);stroke-width:1}
.sbg__base{stroke:rgba(10,10,10,.35);stroke-width:1}
.sbg__ax{font:500 10px 'JetBrains Mono',monospace;fill:#6b6a66}
.sbg__val{font:700 11px 'Switzer',system-ui,sans-serif;fill:#0a0a0a;opacity:0;transition:opacity .3s .9s}
.sbg__col{outline:none;cursor:default}
.sbg__hit{fill:transparent;stroke:none}
.sbg__col:focus-visible .sbg__hit{fill:rgba(10,10,10,.04);stroke:#0a0a0a;stroke-width:1.5}
.sbg__bar{fill:#F5480F;transform-box:fill-box;transform-origin:50% 100%;transform:scaleY(0);transition:transform .7s cubic-bezier(.2,.7,.2,1) calc(var(--i)*70ms),opacity .15s}
.sbg.is-in .sbg__bar{transform:none}
.sbg.is-in .sbg__val{opacity:1}
.sbg.is-hover .sbg__col:not(.is-on) .sbg__bar{opacity:.35}
.sbg.is-hover .sbg__val{opacity:0;transition-delay:0s}
.sbg__tip{position:absolute;z-index:2;pointer-events:none;transform:translate(-50%,-100%);background:#0a0a0a;color:#fff;border-radius:4px;padding:5px 8px;font:500 11px/1.35 'Switzer',system-ui,sans-serif;white-space:nowrap;box-shadow:0 4px 12px rgba(10,10,10,.18)}
.sbg__tip strong{display:block;font-size:13px;font-variant-numeric:tabular-nums}
.sbg__tip span{color:#cfcdc9}
.sbg__sr,.sbg__tbl{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.sbg.is-tbl .sbg__plot{display:none}
.sbg.is-tbl .sbg__tbl{position:static;width:100%;height:auto;clip-path:none;white-space:normal;overflow:visible;border-collapse:collapse;font-size:12px;font-variant-numeric:tabular-nums;margin:auto 0}
.sbg__tbl th,.sbg__tbl td{padding:6px 3px;border-bottom:1px solid rgba(10,10,10,.1);text-align:right}
.sbg__tbl th[scope="row"]{text-align:left;font-weight:600;color:#5f5e5a;font-size:11px}
.sbg__tbl thead th{font:600 11px 'JetBrains Mono',monospace}
.sbg__f{font:500 9.5px 'JetBrains Mono',monospace;color:#6b6a66}
@media(max-width:420px){.sbg{padding:10px 12px 8px}.sbg__f{display:none}.sbg__tbl th[scope="row"]{font-size:10px}.sbg.is-tbl .sbg__tbl{font-size:10.5px}}
@media(prefers-reduced-motion:reduce){.sbg__bar,.sbg__val{transition:none}}// Staafgrafiek met tabel · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.sbg');if(!w)return;var NS='http://www.w3.org/2000/svg';
function el(t,a){var e=document.createElementNS(NS,t);for(var k in a)e.setAttribute(k,a[k]);return e;}
function eur(v){return '€ '+v.toLocaleString('nl-NL');}
var heads=w.querySelectorAll('.sbg__tbl thead th[scope="col"]'),vals=w.querySelectorAll('.sbg__tbl tbody tr:first-child td');
var rows=[].map.call(heads,function(h,i){return {l:h.textContent.trim(),v:+vals[i].dataset.v};});
var W=380,H=176,L=38,R=8,T=16,B=22,max=1000,pw=W-L-R,ph=H-T-B,n=rows.length,step=pw/n,bw=Math.min(30,step*.5);
function delta(i){if(!i)return '';var p=Math.round((rows[i].v-rows[i-1].v)/rows[i-1].v*100);return (p>0?'+':'')+p+'% t.o.v. '+rows[i-1].l;}
var plot=w.querySelector('.sbg__plot'),tip=w.querySelector('.sbg__tip');
var svg=el('svg',{viewBox:'0 0 '+W+' '+H,'class':'sbg__svg',role:'group','aria-label':'Staafgrafiek, pijltjestoetsen lezen per jaar'});
[0,250,500,750,1000].forEach(function(t){var y=T+ph-t/max*ph;svg.appendChild(el('line',{x1:L,x2:W-R,y1:y,y2:y,'class':t?'sbg__grid':'sbg__base'}));var tx=el('text',{x:L-6,y:y+3,'class':'sbg__ax','text-anchor':'end'});tx.textContent=t.toLocaleString('nl-NL');svg.appendChild(tx);});
var cols=rows.map(function(d,i){var cx=L+step*i+step/2,h=d.v/max*ph,x=cx-bw/2,y=T+ph-h,r=Math.min(4,h);
var g=el('g',{'class':'sbg__col',tabindex:'0',role:'img','aria-label':d.l+': '+eur(d.v)+(i?', '+delta(i):'')});g.style.setProperty('--i',i);
g.appendChild(el('rect',{x:cx-step/2+1,y:T-4,width:step-2,height:ph+4,rx:3,'class':'sbg__hit'}));
g.appendChild(el('path',{'class':'sbg__bar',d:'M'+x+','+(T+ph)+'V'+(y+r)+'Q'+x+','+y+' '+(x+r)+','+y+'H'+(x+bw-r)+'Q'+(x+bw)+','+y+' '+(x+bw)+','+(y+r)+'V'+(T+ph)+'Z'}));
var lb=el('text',{x:cx,y:H-6,'class':'sbg__ax','text-anchor':'middle'});lb.textContent=d.l;g.appendChild(lb);
if(i===n-1){var vl=el('text',{x:cx,y:y-6,'class':'sbg__val','text-anchor':'middle'});vl.textContent=eur(d.v);g.appendChild(vl);}
svg.appendChild(g);return {g:g,cx:cx,y:y,d:d,i:i};});
plot.insertBefore(svg,tip);
function show(c){w.classList.add('is-hover');cols.forEach(function(x){x.g.classList.toggle('is-on',x===c);});
tip.innerHTML='<strong>'+eur(c.d.v)+'</strong><span>'+c.d.l+(c.i?' · '+delta(c.i):'')+'</span>';tip.hidden=false;
var m=svg.getScreenCTM(),pr=plot.getBoundingClientRect(),pt=svg.createSVGPoint();pt.x=c.cx;pt.y=c.y;var p=pt.matrixTransform(m);
var tw=tip.offsetWidth,left=Math.max(tw/2,Math.min(pr.width-tw/2,p.x-pr.left)),top=p.y-pr.top-8;tip.style.left=left+'px';tip.style.top=Math.max(tip.offsetHeight,top)+'px';}
function hide(){w.classList.remove('is-hover');tip.hidden=true;cols.forEach(function(x){x.g.classList.remove('is-on');});}
cols.forEach(function(c){c.g.addEventListener('pointerenter',function(){show(c);});c.g.addEventListener('focus',function(){show(c);});c.g.addEventListener('blur',hide);
c.g.addEventListener('keydown',function(e){var k=e.key,j=k==='ArrowRight'?c.i+1:k==='ArrowLeft'?c.i-1:k==='Home'?0:k==='End'?n-1:-2;if(k==='Escape'){hide();return;}if(j>-2){e.preventDefault();cols[Math.max(0,Math.min(n-1,j))].g.focus();}});});
svg.addEventListener('pointerleave',hide);
w.querySelectorAll('.sbg__b').forEach(function(b){b.addEventListener('click',function(){w.querySelectorAll('.sbg__b').forEach(function(x){x.setAttribute('aria-pressed',x===b?'true':'false');});hide();w.classList.toggle('is-tbl',b.dataset.v==='t');});});
if(matchMedia('(prefers-reduced-motion:reduce)').matches||!('IntersectionObserver' in window)){w.classList.add('is-in');}else{var io=new IntersectionObserver(function(es){if(es[0].isIntersecting){w.classList.add('is-in');io.disconnect();}},{threshold:.35});io.observe(w);}
}
init(document.querySelector('.jouw-container'));
Meer uit cijfers & grafieken
Iets gezien dat je mooi vindt?
Zet het in je selectie en stuur hem op. Dan bouwen we je site eromheen, in jouw huisstijl.
Neem contact op →