I am having issues trying to get this chartJS piechart to render in PUG. I would like to put the chartJS code in its own folder and then render it across a PUG template. Can explain what I am doing wrong or point me to some docs that may hep with this? As everyone will be able to see I am attempting to try this in a few different ways. I have placed screen shots in hopes that someone might be able to tell me the cleanest way to go about it, as well as simply getting it to render on the page. I have placed screenshots, please let me know if there is any further info that I could provide. Thanks for any help you can provide.
[app.JS File][1]
const express = require('express');
const bodyParser = require('body-parser')
const path = require('path')
const app = express();
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, 'css')));
app.set('view engine', 'pug')
app.locals.basedir = path.join(__dirname, 'views');
app.get ('/', (req, res) => {
res.render("dashboard", {title: "Home"})
})
app.listen(3000, () => {
console.log('listening to PORT 3000')
})
[JavaScript Pug Code][2]
$( document ).ready(function () {
var ctx = document.getElementById("myChart").getContext('2d');
var chart = new Chart(ctx, {
type: 'pie',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255,99,132,1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
})
[Main Dashboard Page][3]
html(lang="en")
head
meta(charset="UTF-8")
meta(name="viewport", content="width=device-width, initial-scale=1.0")
title USRA-NASA-NAMS
link(rel="stylesheet" href="./insight.css")
script(src="https://code.jquery.com/jquery-3.5.0.min.js")
script(src='https://cdn.jsdelivr.net/npm/chart.js#2.8.0')
script(type="text/javascript" src='pieChart.js')
<!DOCTYPE html>
<html>
<body>
<div id="chartContainer" style="height: 300px; width: 100%;">
<script type="text/javascript">
window.onload = function () {
var chart = new CanvasJS.Chart("chartContainer");
chart.options.axisY = { prefix: "$", suffix: "K" };
chart.options.title = { text: "Fruits sold in First & Second Quarter" };
var series1 = { //dataSeries - first quarter
type: "column",
name: "First Quarter",
showInLegend: true
};
var series2 = { //dataSeries - second quarter
type: "column",
name: "Second Quarter",
showInLegend: true
};
chart.options.data = [];
chart.options.data.push(series1);
chart.options.data.push(series2);
series1.dataPoints = [
{ label: "banana", y: 58 },
{ label: "orange", y: 69 },
{ label: "apple", y: 80 },
{ label: "mango", y: 74 },
{ label: "grape", y: 64 }
];
series2.dataPoints = [
{ label: "banana", y: 63 },
{ label: "orange", y: 73 },
{ label: "apple", y: 88 },
{ label: "mango", y: 77 },
{ label: "grape", y: 60 }
];
chart.render();
}
[Pug Page with PieChart Code][4]
div(class="chart")
canvas(id="chartPic" width="400" height="400")
script(src="chart.js")
script.
-window.onload = function() {
-var red="#{red}", green="#{green}", blue="#{blue}";
-var ctx = document.getElementById("chartPic").getContext('2d');
-var chart = new Chart(ctx, {
-type: 'pie',
-data: {
-labels: ["red", "green", "blue"],
-datasets: [{
-label: 'Number of votes',
-data: [1, 1, 1],
-backgroundColor: [red, green, blue],
-borderColor: [green, blue, red],
-borderWidth: 1
}],
},
-options: {
-title: {
-display: true,
-text: "chart",
},
-legend: {
-position: 'bottom'
},
}
});
};
Related
I'm migrating some plots from Plot.ly to Chart.js v2.9, and I'm trying to add background colors to my new Chart.js plots to match their predecessor:
But when I add the appropriate plugin to my Chart.js config, instead I get this:
Here is my plugin code:
[{
beforeDraw: function (chart, easing) {
let config: DragenChartConfiguration = chart.config;
if (chart.ctx) {
if (config.dragen?.hBars) {
var ctx = chart.ctx;
var chartArea = chart.chartArea;
for (const hBar of config.dragen.hBars) {
ctx.save();
ctx.fillStyle = hBar.color;
ctx.fillRect(chartArea.left, hBar.from, chartArea.right - chartArea.left, hBar.to - hBar.from);
ctx.restore();
}
}
}
}
}]
Where each "Hbar" object simply defines a color and a Y-axis range I want to color:
hBars: Array(3)
0: {from: 28, to: 100, color: "rgb(195, 230, 195)"}
1: {from: 20, to: 28, color: "rgb(230, 220, 195)"}
2: {from: 0, to: 20, color: "rgb(230, 195, 195)"}
length: 3
What am I missing here?
You are taking raw values instead of getting the pixels for those values, if you do that it will work:
Plugin V3:
{
id: 'backgrounds',
beforeDraw: (chart, args, options) => {
const { ctx, chartArea, scales: {y} } = chart;
options.hbars.forEach((hBar) => {
ctx.save();
ctx.fillStyle = hBar.color;
ctx.fillRect(chartArea.left, y.getPixelForValue(hBar.from), chartArea.right - chartArea.left, y.getPixelForValue(hBar.to) - y.getPixelForValue(hBar.from));
ctx.restore();
})
}
}
Plugin V2:
{
id: 'backgrounds',
beforeDraw: (chart, x, options) => {
const { ctx, chartArea, scales } = chart;
const y = scales['y-axis-0']
options.hbars.forEach((hBar) => {
ctx.save();
ctx.fillStyle = hBar.color;
ctx.fillRect(chartArea.left, y.getPixelForValue(hBar.from), chartArea.right - chartArea.left, y.getPixelForValue(hBar.to) - y.getPixelForValue(hBar.from));
ctx.restore();
})
}
Working example V3:
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [100, 19, 3, 5, 2, 3],
borderWidth: 1
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderWidth: 1
}
]
},
options: {
plugins: {
backgrounds: {
hbars: [{
from: 28,
to: 100,
color: "rgb(195, 230, 195)"
},
{
from: 20,
to: 28,
color: "rgb(230, 220, 195)"
},
{
from: 0,
to: 20,
color: "rgb(230, 195, 195)"
}
]
}
}
},
plugins: [{
id: 'backgrounds',
beforeDraw: (chart, args, options) => {
const {
ctx,
chartArea,
scales: {
y
}
} = chart;
options.hbars.forEach((hBar) => {
ctx.save();
ctx.fillStyle = hBar.color;
ctx.fillRect(chartArea.left, y.getPixelForValue(hBar.from), chartArea.right - chartArea.left, y.getPixelForValue(hBar.to) - y.getPixelForValue(hBar.from));
ctx.restore();
})
}
}]
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.2.0/chart.js"></script>
</body>
Working example V2:
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [100, 19, 3, 5, 2, 3],
borderWidth: 1
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderWidth: 1
}
]
},
options: {
plugins: {
backgrounds: {
hbars: [{
from: 28,
to: 100,
color: "rgb(195, 230, 195)"
},
{
from: 20,
to: 28,
color: "rgb(230, 220, 195)"
},
{
from: 0,
to: 20,
color: "rgb(230, 195, 195)"
}
]
}
}
},
plugins: [{
id: 'backgrounds',
beforeDraw: (chart, x, options) => {
const {
ctx,
chartArea,
scales
} = chart;
const y = scales['y-axis-0']
options.hbars.forEach((hBar) => {
ctx.save();
ctx.fillStyle = hBar.color;
ctx.fillRect(chartArea.left, y.getPixelForValue(hBar.from), chartArea.right - chartArea.left, y.getPixelForValue(hBar.to) - y.getPixelForValue(hBar.from));
ctx.restore();
})
}
}]
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js"></script>
</body>
Fiddle V3: https://jsfiddle.net/Leelenaleee/6s8upz10/15/
Fiddle V2: https://jsfiddle.net/Leelenaleee/tj71gLa2/10/
I am testing out HighCharts and ChartJS to see which to use. For Highcharts, I was able to find a hack to create a bar chart that had double grouping on the x-axis.
This is what I want to look like:
I am new to both charting JS options and wonder if there is a way to do this in ChartJS.
I have the datasets something like this:
xAxis: {
categories: [{
name: "Total",
categories: ["2004", "2008", "2012"]
}, {
name: "Lower than 2.50",
categories: ["2004", "2008", "2012"]
}]
},
yAxis: {
min: 0,
title: {
text: 'Percent (%)'
}
},
series: [{
name: 'Male',
data: [42.4, 43.0, 43.0, 50.3, 49.4, 48.4]
}, {
name: 'Female',
data: [57.6, 57.0, 57.0, 49.7, 50.6, 51.6]
}]
Essentially I need a nest series on the x-axis and I am open to plugins or code from Github to do this.
You can make use of the Plugin Core API. It offers different hooks that may be used for executing custom code. In below code, I use the afterDraw hook to draw the category labels and the delimiter lines.
new Chart('myChart', {
type: 'bar',
plugins: [{
afterDraw: chart => {
let ctx = chart.chart.ctx;
ctx.save();
let xAxis = chart.scales['x-axis-0'];
let xCenter = (xAxis.left + xAxis.right) / 2;
let yBottom = chart.scales['y-axis-0'].bottom;
ctx.textAlign = 'center';
ctx.font = '12px Arial';
ctx.fillText(chart.data.categories[0], (xAxis.left + xCenter) / 2, yBottom + 40);
ctx.fillText(chart.data.categories[1], (xCenter + xAxis.right) / 2, yBottom + 40);
ctx.strokeStyle = 'lightgray';
[xAxis.left, xCenter, xAxis.right].forEach(x => {
ctx.beginPath();
ctx.moveTo(x, yBottom);
ctx.lineTo(x, yBottom + 40);
ctx.stroke();
});
ctx.restore();
}
}],
data: {
labels: ['2004', '2008', '2012', '2016', '2004', '2008', '2012', '2016'],
categories: ['Total', 'Lower than 2.50'],
datasets: [{
label: 'Male',
data: [42.4, 43.0, 43.0, 50.3, 49.4, 48.4, 51.2, 51.8],
backgroundColor: 'rgba(124, 181, 236, 0.9)',
borderColor: 'rgb(124, 181, 236)',
borderWidth: 1
},
{
label: 'Female',
data: [57.6, 57.0, 57.0, 49.7, 50.6, 51.6, 53.7, 54.6],
backgroundColor: 'rgba(67, 67, 72, 0.9)',
borderColor: 'rgb(67, 67, 72)',
borderWidth: 1
}
]
},
options: {
legend: {
position: 'bottom',
labels: {
padding: 30,
usePointStyle: true
}
},
scales: {
yAxes: [{
ticks: {
min: 0,
max: 80,
stepSize: 20
},
scaleLabel: {
display: true,
labelString: 'Percent (%)'
}
}],
xAxes: [{
gridLines: {
drawOnChartArea: false
}
}]
}
}
});
canvas {
max-width: 400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="200"></canvas>
You have to define a second x-axis and play around with the many ticks and gridLines options.
Please take a look at below runnable code and see how it could be done. This is obviously only a draft and needs to optimized and made more generic.
new Chart(document.getElementById('myChart'), {
type: 'bar',
data: {
labels: ['2004', '2008', '2012', '2004', '2008', '2012'],
datasets: [{
label: 'Male',
data: [42.4, 43.0, 43.0, 50.3, 49.4, 48.4],
backgroundColor: 'rgba(124, 181, 236, 0.9)',
borderColor: 'rgb(124, 181, 236)',
borderWidth: 1
},
{
label: 'Female',
data: [57.6, 57.0, 57.0, 49.7, 50.6, 51.6],
backgroundColor: 'rgba(67, 67, 72, 0.9)',
borderColor: 'rgb(67, 67, 72)',
borderWidth: 1
}
]
},
options: {
legend: {
position: 'bottom',
labels: {
usePointStyle: true
}
},
scales: {
yAxes: [{
ticks: {
min: 0,
max: 80,
stepSize: 20
},
scaleLabel: {
display: true,
labelString: 'Percent (%)'
}
}],
xAxes: [{
gridLines: {
drawOnChartArea: false
}
},
{
offset: true,
ticks: {
autoSkip: false,
maxRotation: 0,
padding: -15,
callback: (v, i) => {
if (i == 1) {
return 'Total';
} else if (i == 4) {
return 'Lower than 2.50';
} else {
return '';
}
}
},
gridLines: {
drawOnChartArea: false,
offsetGridLines: true,
tickMarkLength: 20,
color: ['white', 'white', 'white', 'lightgray', 'white', 'white', 'lightgray']
}
}
]
}
}
});
canvas {
max-width: 400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="200"></canvas>
I succesfully added second set of labels by following other question on SO.
But now I want to show legend for all labels and second set appears crossed out. How to avoid that?
Here is my attempt:
https://jsfiddle.net/L5gs39u2/1/
var platform_labels = ["Tablet","Ordenador"];
var platform_dataset = [14,5];
var os_labels = ["Android","Windows","GNU\/Linux"];
var os_dataset = [14,4,1];
var devices_labels = ["Tablet","Ordenador","Android","Windows","GNU\/Linux"];
var chartColors = {
red: 'rgb(255, 99, 132)',
orange: 'rgb(255, 159, 64)',
yellow: 'rgb(255, 205, 86)',
green: 'rgb(75, 192, 192)',
blue: 'rgb(54, 162, 235)',
purple: 'rgb(153, 102, 255)',
grey: 'rgb(201, 203, 207)'
};
var config = {
type: 'doughnut',
data: {
datasets: [{
data: platform_dataset,
backgroundColor: [
chartColors.red,
chartColors.orange,
chartColors.yellow
],
label: 'Platform',
labels: platform_labels
}, {
data: os_dataset,
backgroundColor: [
chartColors.purple,
chartColors.green,
chartColors.blue
],
label: 'OS',
labels: os_labels
}],
labels: devices_labels
},
options: {
tooltips: {
callbacks: {
label: function (tooltipItem, data) {
var dataset = data.datasets[tooltipItem.datasetIndex];
var index = tooltipItem.index;
return dataset.labels[index] + ": " + dataset.data[index];
}
}
}
}
};
var ctx = document.getElementById('deviceChart').getContext('2d');
var employeesGraph = new Chart(ctx, config);
You have to generate the legend labels yourself by defining a legend.labels.generateLabels function together with a legend.onClick function that takes care of hiding and showing individual pie slices. This could look as follows:
legend: {
labels: {
generateLabels: () => {
let labels = [];
config.data.datasets.forEach((ds, iDs) => labels = labels.concat(ds.labels.map((l, iLabel) => ({
datasetIndex: iDs,
labelIndex: iLabel,
text: l,
fillStyle: ds.backgroundColor[iLabel],
hidden: employeesGraph ? employeesGraph.getDatasetMeta(iDs).data[iLabel].hidden : false,
strokeStyle: '#fff'
}))));
return labels;
}
},
onClick: (event, legendItem) => {
const metaData = employeesGraph.getDatasetMeta(legendItem.datasetIndex).data;
metaData[legendItem.labelIndex].hidden = !metaData[legendItem.labelIndex].hidden;
employeesGraph.update();
}
},
Please have a look at your amended code below:
var platform_labels = ["Tablet", "Ordenador"];
var platform_dataset = [14, 5];
var os_labels = ["Android", "Windows", "GNU\/Linux"];
var os_dataset = [14, 4, 1];
var devices_labels = ["Tablet", "Ordenador", "Android", "Windows", "GNU\/Linux"];
var chartColors = {
red: 'rgb(255, 99, 132)',
orange: 'rgb(255, 159, 64)',
yellow: 'rgb(255, 205, 86)',
green: 'rgb(75, 192, 192)',
blue: 'rgb(54, 162, 235)',
purple: 'rgb(153, 102, 255)',
grey: 'rgb(201, 203, 207)'
};
var config = {
type: 'doughnut',
data: {
datasets: [{
data: platform_dataset,
backgroundColor: [
chartColors.red,
chartColors.orange,
],
label: 'Platform',
labels: platform_labels
}, {
data: os_dataset,
backgroundColor: [
chartColors.purple,
chartColors.green,
chartColors.blue
],
label: 'OS',
labels: os_labels
}],
labels: devices_labels
},
options: {
legend: {
labels: {
generateLabels: () => {
let labels = [];
config.data.datasets.forEach((ds, iDs) => labels = labels.concat(ds.labels.map((l, iLabel) => ({
datasetIndex: iDs,
labelIndex: iLabel,
text: l,
fillStyle: ds.backgroundColor[iLabel],
hidden: employeesGraph ? employeesGraph.getDatasetMeta(iDs).data[iLabel].hidden : false,
strokeStyle: '#fff'
}))));
return labels;
}
},
onClick: (event, legendItem) => {
const metaData = employeesGraph.getDatasetMeta(legendItem.datasetIndex).data;
metaData[legendItem.labelIndex].hidden = !metaData[legendItem.labelIndex].hidden;
employeesGraph.update();
}
},
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var dataset = data.datasets[tooltipItem.datasetIndex];
var index = tooltipItem.index;
return dataset.labels[index] + ": " + dataset.data[index];
}
}
}
}
};
var ctx = document.getElementById('deviceChart').getContext('2d');
var employeesGraph = new Chart(ctx, config);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.js"></script>
<canvas id="deviceChart" height="120"></canvas>
The second set of labels are striked through because chartjs see's them as hidden, i.e. they have no values so are not rendered and the labels are only getting drawn from the first dataset.
There is a few ways around this, you could either disable the legend and then create a custom legend but I'm not sure if this will grab all of the labels:
var chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
legend: {
display: false
},
legendCallback: function(chart) {
var text = [];
text.push('<ul>');
for (var i=0; i<devices_labels.length; i++) {
text.push('<li>');
text.push('<span style="background-color:' +
chart.data.datasets[i].borderColor + '">' + devices_labels[i] +
'</span>');
text.push('</li>');
}
text.push('</ul>');
return text.join("");
}
}
});
Or you could extend chartjs to change the behaviour of labels for hidden data:- change legend item style when dataset is hidden
Or the simplest way is to just add in some dummy data, to your first dataset:-
var platform_labels = ["Tablet","Ordenador"];
var platform_dataset = [14, 5, 0, 0, 0];
var os_labels = ["Android","Windows","GNU\/Linux"];
var os_dataset = [14,4,1];
var devices_labels = ["Tablet","Ordenador","Android","Windows","GNU\/Linux"];
var chartColors = {
red: 'rgb(255, 99, 132)',
orange: 'rgb(255, 159, 64)',
yellow: 'rgb(255, 205, 86)',
green: 'rgb(75, 192, 192)',
blue: 'rgb(54, 162, 235)',
purple: 'rgb(153, 102, 255)',
grey: 'rgb(201, 203, 207)'
};
var config = {
type: 'doughnut',
data: {
datasets: [{
data: platform_dataset,
backgroundColor: [
chartColors.red,
chartColors.orange,
chartColors.purple,
chartColors.green,
chartColors.blue
],
label: 'Platform',
labels: platform_labels
}, {
data: os_dataset,
backgroundColor: [
chartColors.purple,
chartColors.green,
chartColors.blue
],
label: 'OS',
labels: os_labels
}],
labels: devices_labels
}
};
var ctx = document.getElementById('deviceChart').getContext('2d');
var employeesGraph = new Chart(ctx, config);
Who know how to draw lines and points inside a bar something like this:
You can draw the lines as floating bars where individual bars are specified with the syntax [min, max], same as you already do for your your other bars. Given an array of number values, the "line" data can be produced with Array.map() as follows.
data: [110, 150, 140, 100, 120].map(v => [v - 1, v + 1])
Then you need to define individual stacked xAxes for the "bar" and the "line" datasets. In order to have the original "line" values displayed in the tooltips, a toolips.callback.label function is also needed.
The points can be defined in an additional dataset of type 'scatter'.
Please have a look at below runnable code snippet.
new Chart("chart", {
type: "bar",
data: {
labels: ["4", "3", "2", "1", "0"],
datasets: [{
label: "Bars",
backgroundColor: "rgba(0, 255, 0, 0.2)",
data: [[20, 100], [50, 180], [60, 120], [10, 130], [70, 140]],
xAxisID: "x-axis-actual",
order: 1
},
{
label: "Lines",
backgroundColor: "rgb(0, 0, 0)",
data: [90, 150, 110, 90, 120].map(v => [v - 1, v + 1]),
xAxisID: "x-axis-target",
order: 2
},
{
label: "Points",
backgroundColor: "rgb(255, 255, 255)",
borderColor: "rgb(0, 0, 0)",
data: [80, 110, 90, 100, 120],
type: "scatter"
}
]
},
options: {
legend: {
display: false
},
tooltips: {
callbacks: {
label: (tooltipItem, data) => {
const dataset = data.datasets[tooltipItem.datasetIndex];
const v = dataset.data[tooltipItem.index];
return dataset.label + ': ' + (tooltipItem.datasetIndex == 1 ? (v[1] + v[0]) / 2 : tooltipItem.value);
}
}
},
scales: {
xAxes: [{
id: "x-axis-target",
stacked: true
},
{
display: false,
offset: true,
stacked: true,
id: "x-axis-actual",
gridLines: {
offsetGridLines: true
}
}
],
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart" height="120"></canvas>
i would like to ask is it possible to display data values on a half donuts chart? Want to show the data value as text at the bottom of the chart by taking its value from the array
here is the link: https://jsfiddle.net/z638ttv0/16/
Thanks for the help in advance
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ["Red"],
datasets: [{
label: '# of Votes',
data: [50,100-50],
backgroundColor: [
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
rotation: 1 * Math.PI,
circumference: 1 * Math.PI
}
});
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ["Red"],
datasets: [{
label: '# of Votes',
data: [50,100-50],
text: "ff",
backgroundColor: [
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
rotation: 1 * Math.PI,
circumference: 1 * Math.PI
}
});
.test
{
position:absolute;
bottom:-20px;
left:calc(50% - 10px);
display:block;
}
<body>
<span class="test">test</span>
<canvas id="myChart" width="400" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.4/Chart.min.js"></script>
</body>
Customised value