Tre diagrammer basert på SSB-tabellene 14068, 14059 og 14065: - Kakediagram: 34 % eier fritt, 38 % eier med banklån, 28 % leier - Stablet stolpe: gjeldsbyrde over tid (lån >3M tredoblet seg) - Horisontal stablet: eierskap etter husholdningstype (65 % av unge leier) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
248 lines
7.1 KiB
JavaScript
248 lines
7.1 KiB
JavaScript
// SSB data: Boligeierskap og boligøkonomi
|
||
// Tabell 14068: Boligeierskap etter husholdningstype (prosent)
|
||
// Tabell 14059: Boligøkonomi etter eierstatus (gjeldsstørrelse)
|
||
// Tabell 14065: Boliglån og boligutgifter (gjeld vs verdi)
|
||
// Hentet 2026-03-16 via SSB PxWeb API v2
|
||
|
||
// --- Data ---
|
||
|
||
// Eierskap alle husholdninger 2024 (tabell 14068):
|
||
// Selveier 57.5%, Andelseier 14.8%, Leier 27.7%
|
||
// Total eiere = 72.3%
|
||
// Av eiere uten boliglån: 47.6% (tabell 14059, 2024)
|
||
// Eier fritt = 72.3 × 0.476 = 34.4%
|
||
// Eier med lån = 72.3 × 0.524 = 37.9%
|
||
|
||
// Gjeldsstørrelse blant eiere med lån (tabell 14059, "I alt")
|
||
const YEARS = ['2015', '2016', '2017', '2018', '2019', '2020', '2021', '2022', '2023', '2024'];
|
||
|
||
const LOAN_DIST = {
|
||
under1M: [17.2, 15.3, 13.9, 13.5, 13.2, 12.7, 13.6, 12.4, 11.7, 12.1],
|
||
from1to2M:[17.0, 16.8, 16.2, 15.1, 14.9, 14.9, 14.2, 14.8, 14.4, 14.8],
|
||
from2to3M:[ 8.2, 9.7, 10.0, 9.7, 10.3, 10.5, 12.1, 12.1, 11.9, 11.8],
|
||
over3M: [ 4.5, 5.1, 6.2, 6.8, 8.1, 8.9, 10.9, 12.0, 12.2, 13.8]
|
||
};
|
||
|
||
// Eierskap etter husholdningstype 2024 (tabell 14068)
|
||
const HOUSEHOLD_TYPES = [
|
||
{ name: 'Aleneboende 16–44', selveier: 20.7, andel: 13.8, leier: 65.5 },
|
||
{ name: 'Par uten barn 16–44', selveier: 41.6, andel: 21.8, leier: 36.6 },
|
||
{ name: 'Enslige forsørgere 0–17', selveier: 50.1, andel: 11.6, leier: 38.3 },
|
||
{ name: 'Aleneboende 45–66', selveier: 54.7, andel: 17.2, leier: 28.1 },
|
||
{ name: 'Aleneboende 67+', selveier: 60.0, andel: 23.0, leier: 17.0 },
|
||
{ name: 'Par med barn 0–6', selveier: 70.8, andel: 12.1, leier: 17.1 },
|
||
{ name: 'Par uten barn 67+', selveier: 78.7, andel: 15.2, leier: 6.1 },
|
||
{ name: 'Par med barn 7–17', selveier: 82.9, andel: 8.2, leier: 8.8 },
|
||
{ name: 'Par uten barn 45–66', selveier: 82.2, andel: 11.6, leier: 6.1 },
|
||
{ name: 'Par med barn 18+', selveier: 87.7, andel: 6.8, leier: 5.6 },
|
||
];
|
||
|
||
// --- Formattering ---
|
||
|
||
function fmtPct(n) {
|
||
return n.toLocaleString('nb-NO', { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + '\u202f%';
|
||
}
|
||
|
||
const GRID_COLOR = 'rgba(0,0,0,0.06)';
|
||
const TICK_COLOR = '#8a857e';
|
||
|
||
function baseOpts() {
|
||
return {
|
||
responsive: true, maintainAspectRatio: false,
|
||
plugins: {
|
||
legend: { display: false },
|
||
tooltip: {
|
||
backgroundColor: '#1a1714',
|
||
titleColor: '#f5f2eb',
|
||
bodyColor: 'rgba(245,242,235,0.7)',
|
||
padding: 10,
|
||
cornerRadius: 4,
|
||
titleFont: { family: 'DM Sans', size: 12 },
|
||
bodyFont: { family: 'DM Sans', size: 12 }
|
||
}
|
||
},
|
||
scales: {
|
||
x: {
|
||
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, maxRotation: 0 },
|
||
grid: { display: false },
|
||
border: { color: 'rgba(0,0,0,0.1)' }
|
||
},
|
||
y: {
|
||
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, callback: v => v + ' %' },
|
||
grid: { color: GRID_COLOR },
|
||
border: { dash: [3, 3], color: 'transparent' }
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
// --- Diagram 1: Kakediagram (doughnut) ---
|
||
|
||
new Chart(document.getElementById('chart1'), {
|
||
type: 'doughnut',
|
||
data: {
|
||
labels: ['Eier fritt (34 %)', 'Eier med lån (38 %)', 'Leier (28 %)'],
|
||
datasets: [{
|
||
data: [34.4, 37.9, 27.7],
|
||
backgroundColor: ['#2c6e49', '#1a4a8a', '#c0392b'],
|
||
borderColor: '#fff',
|
||
borderWidth: 3,
|
||
hoverOffset: 8
|
||
}]
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
cutout: '55%',
|
||
plugins: {
|
||
legend: { display: false },
|
||
tooltip: {
|
||
callbacks: {
|
||
label: function(c) {
|
||
return ' ' + c.label + ': ' + fmtPct(c.parsed);
|
||
}
|
||
},
|
||
backgroundColor: '#1a1714',
|
||
titleColor: '#f5f2eb',
|
||
bodyColor: 'rgba(245,242,235,0.7)',
|
||
padding: 10,
|
||
cornerRadius: 4,
|
||
titleFont: { family: 'DM Sans', size: 12 },
|
||
bodyFont: { family: 'DM Sans', size: 12 }
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
// --- Diagram 2: Gjeldsbyrde over tid (stablet stolpe) ---
|
||
|
||
var chart2Opts = baseOpts();
|
||
chart2Opts.scales.y.stacked = true;
|
||
chart2Opts.scales.x.stacked = true;
|
||
chart2Opts.scales.y.max = 55;
|
||
chart2Opts.plugins.tooltip = {
|
||
mode: 'index',
|
||
callbacks: {
|
||
label: function(c) {
|
||
return ' ' + c.dataset.label + ': ' + fmtPct(c.parsed.y);
|
||
}
|
||
},
|
||
backgroundColor: '#1a1714',
|
||
titleColor: '#f5f2eb',
|
||
bodyColor: 'rgba(245,242,235,0.7)',
|
||
padding: 10,
|
||
cornerRadius: 4,
|
||
titleFont: { family: 'DM Sans', size: 12 },
|
||
bodyFont: { family: 'DM Sans', size: 12 }
|
||
};
|
||
|
||
new Chart(document.getElementById('chart2'), {
|
||
type: 'bar',
|
||
data: {
|
||
labels: YEARS,
|
||
datasets: [
|
||
{
|
||
label: 'Under 1 mill',
|
||
data: LOAN_DIST.under1M,
|
||
backgroundColor: '#27ae60cc',
|
||
borderColor: '#27ae60',
|
||
borderWidth: 1,
|
||
borderRadius: 1
|
||
},
|
||
{
|
||
label: '1–2 mill',
|
||
data: LOAN_DIST.from1to2M,
|
||
backgroundColor: '#e67e22cc',
|
||
borderColor: '#e67e22',
|
||
borderWidth: 1,
|
||
borderRadius: 1
|
||
},
|
||
{
|
||
label: '2–3 mill',
|
||
data: LOAN_DIST.from2to3M,
|
||
backgroundColor: '#e74c3ccc',
|
||
borderColor: '#e74c3c',
|
||
borderWidth: 1,
|
||
borderRadius: 1
|
||
},
|
||
{
|
||
label: 'Over 3 mill',
|
||
data: LOAN_DIST.over3M,
|
||
backgroundColor: '#8b1a1acc',
|
||
borderColor: '#8b1a1a',
|
||
borderWidth: 1,
|
||
borderRadius: 1
|
||
}
|
||
]
|
||
},
|
||
options: chart2Opts
|
||
});
|
||
|
||
// --- Diagram 3: Eierskap etter livssituasjon (horisontal stablet) ---
|
||
|
||
var chart3Opts = baseOpts();
|
||
chart3Opts.indexAxis = 'y';
|
||
chart3Opts.scales = {
|
||
x: {
|
||
stacked: true,
|
||
max: 100,
|
||
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, callback: v => v + ' %' },
|
||
grid: { color: GRID_COLOR },
|
||
border: { dash: [3, 3], color: 'transparent' }
|
||
},
|
||
y: {
|
||
stacked: true,
|
||
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, autoSkip: false },
|
||
grid: { display: false },
|
||
border: { color: 'rgba(0,0,0,0.1)' }
|
||
}
|
||
};
|
||
chart3Opts.plugins.tooltip = {
|
||
mode: 'index',
|
||
callbacks: {
|
||
label: function(c) {
|
||
return ' ' + c.dataset.label + ': ' + fmtPct(c.parsed.x);
|
||
}
|
||
},
|
||
backgroundColor: '#1a1714',
|
||
titleColor: '#f5f2eb',
|
||
bodyColor: 'rgba(245,242,235,0.7)',
|
||
padding: 10,
|
||
cornerRadius: 4,
|
||
titleFont: { family: 'DM Sans', size: 12 },
|
||
bodyFont: { family: 'DM Sans', size: 12 }
|
||
};
|
||
|
||
new Chart(document.getElementById('chart3'), {
|
||
type: 'bar',
|
||
data: {
|
||
labels: HOUSEHOLD_TYPES.map(h => h.name),
|
||
datasets: [
|
||
{
|
||
label: 'Selveier',
|
||
data: HOUSEHOLD_TYPES.map(h => h.selveier),
|
||
backgroundColor: '#2c6e49cc',
|
||
borderColor: '#2c6e49',
|
||
borderWidth: 1,
|
||
borderRadius: 2
|
||
},
|
||
{
|
||
label: 'Andelseier',
|
||
data: HOUSEHOLD_TYPES.map(h => h.andel),
|
||
backgroundColor: '#1a4a8acc',
|
||
borderColor: '#1a4a8a',
|
||
borderWidth: 1,
|
||
borderRadius: 2
|
||
},
|
||
{
|
||
label: 'Leier',
|
||
data: HOUSEHOLD_TYPES.map(h => h.leier),
|
||
backgroundColor: '#c0392bcc',
|
||
borderColor: '#c0392b',
|
||
borderWidth: 1,
|
||
borderRadius: 2
|
||
}
|
||
]
|
||
},
|
||
options: chart3Opts
|
||
});
|