Metadata in SVG
author(s):
Øystein Moseng
by oysteinmoseng
HTML
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/series-label.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/export-data.js"></script>
<script src="https://code.highcharts.com/modules/accessibility.js"></script>
<h1>Metadata embedded in SVG</h1>
<figure class="highcharts-figure">
<div id="container-bar"></div>
<div>
<h2>JSON</h2>
<pre class="json" id="bar-json"></pre>
</div>
</figure>
<figure class="highcharts-figure">
<div id="container-line"></div>
<div>
<h2>JSON</h2>
<pre class="json" id="line-json"></pre>
</div>
</figure>
CSS
body {
font-family: sans-serif;
}
h2 {
font-size: 0.8rem;
}
.json {
background-color: #f8f8f8;
border: 1px solid #ccc;
border-radius: 8px;
padding: 10px;
width: 600px;
overflow: scroll;
}
#container-line {
height: 480px;
}
#line-json {
height: 430px;
}
#container-bar {
height: 400px;
}
#bar-json {
height: 350px;
}
#container-line,
#container-bar {
flex-grow: 1;
min-width: 450px;
}
.highcharts-figure {
margin-bottom: 50px;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 20px;
max-width: 1400px;
}
.highcharts-data-table table {
min-width: 450px;
font-family: Verdana, sans-serif;
border-collapse: collapse;
border: 1px solid #ebebeb;
margin: 10px auto;
text-align: center;
width: 100%;
max-width: 500px;
}
.highcharts-data-table caption {
padding: 1em 0;
font-size: 1.2em;
color: #555;
}
.highcharts-data-table th {
font-weight: 600;
padding: 0.5em;
}
.highcharts-data-table td,
.highcharts-data-table th,
.highcharts-data-table caption {
padding: 0.5em;
}
.highcharts-data-table thead tr,
.highcharts-data-table tr:nth-child(even) {
background: #f8f8f8;
}
.highcharts-data-table tr:hover {
background: #f1f7ff;
}
JavaScript
// ---------------------------------------------------------------
// Wrapper for embedding metadata in the SVG
// ---------------------------------------------------------------
function getPointMetadata(point) {
return {
x: Highcharts.pick(point.name || point.category, point.x),
y: point.y
};
}
function getSeriesMetadata(series) {
return {
name: series.name,
type: series.type,
data: series.points.map(getPointMetadata)
};
}
function getChartMetadataJSON(chart) {
const metadata = {
title: chart.options.title.text,
subtitle: chart.options.subtitle.text,
description: chart.options.accessibility.description,
series: chart.series.map(getSeriesMetadata)
};
return JSON.stringify(metadata, null, ' ');
}
// Store metadata in the SVG as a custom element under the metadata element
// The metadata is stored as raw JSON.
Highcharts.addEvent(Highcharts.Chart, 'render', function () {
const chart = this;
if (!chart.metadata) {
const container = document.createElementNS('http://www.w3.org/2000/svg','metadata'),
xmlns = 'http://www.highcharts.com/xmlns/svgmetadata';
chart.renderer.box.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:svgmetadata', xmlns);
chart.metadata = document.createElementNS(xmlns, 'svgmetadata:json');
container.appendChild(chart.metadata);
chart.renderer.box.appendChild(container);
}
chart.metadata.textContent = getChartMetadataJSON(chart);
});
// Output the JSON to make it visible below the charts
Highcharts.addEvent(Highcharts.Chart, 'render', function () {
const outID = this.options.chart.type === 'column' ? 'bar-json' : 'line-json',
outEl = document.getElementById(outID);
outEl.textContent = this.metadata.textContent;
});
// ---------------------------------------------------------------
// Demonstrate by creating some charts
// ---------------------------------------------------------------
//...