I have a bar chart where I have drawn 3 vertical lines, each with it's own label at the top. I would like those labels to be above the top of the y-axis (above the 30% line in the example) but below the legend. I can't figure out how to increase the space between the top legend and the chart such that I can have my vertical line labels (15, 24 & 33) be off of the chart itself but below the legend. Any ideas?
If you want do increase spacing in all charts you can put this code before creating :
Chart.Legend.prototype.afterFit = function() {
this.height = this.height + 50;
};
Of course, I don't try but i think you can change it (or copy the original Chart object before, to keep the original padding).
Bye,
If you want to apply padding below legend for some charts only in your app:
ChartJS >= 2.1.0
Chart.plugins.register({
id: 'paddingBelowLegends',
beforeInit: function(chart, options) {
chart.legend.afterFit = function() {
this.height = this.height + 50;
};
}
});
// ----------------------------------
// disable the plugin only for charts
// where you DO NOT WANT the padding
// ----------------------------------
// for raw ChartJS use:
var chart = new Chart(ctx, {
config: {
plugins: {
paddingBelowLegends: false
}
}
});
// for angular-chartjs:
$scope.myChart.options.plugins = { paddingBelowLegends: false }
// then in template:
// <canvas class="chart ..." chart-options="myChart.options" ... />
ChartJS >= 2.5.0
Specific plugins for each chart are supported, it should be possible to do:
var chart = new Chart(ctx, {
plugins: [{
beforeInit: function(chart, options) {
chart.legend.afterFit = function() {
this.height = this.height + 50;
};
}
}]
});
See ChartJS documentation + inspired by this other answer
Unfortunately, since there is no config option to handle this the only way you can achieve the desired result is to extend Chart.Legend and implement the afterFit() callback.
Here is a quick codepen showing how to do just that. To change the spacing, just change the value in line 9 (currently set to 50). Also, this of course only works with the legend at the top. Hopefully, the example is clear enough for you to modify in case you want to move your legend elsewhere.
If anyone is wondering why the afterFit solution is not working in Chart.js 3.3.0 it is because afterFit function was removed from the legend plugin.
If you want to make this work anyway by taking advantage over the fit function, you can try this hacky solution / workaround:
const plugin = {
beforeInit(chart) {
// Get reference to the original fit function
const originalFit = chart.legend.fit;
// Override the fit function
chart.legend.fit = function fit() {
// Call original function and bind scope in order to use `this` correctly inside it
originalFit.bind(chart.legend)();
// Change the height as suggested in another answers
this.height += 15;
}
};
}
I know that this is not an ideal solution, but until we have native support for this legend padding, I'm afraid this is as good we can do right now.
This helped me after 2 days of research.
Chart.Legend.prototype.afterFit = function() {
this.height = this.height + 50;
};
update this in module.ts file
I'm using react-chartjs-2 (but this is just a port and uses the same configurations object) and I was able to achieve that by changing the labels configuration nested on legend configuration:
chartOptions = {
legend: {
labels: {
padding: 50 -> this one.
}
},
You can check the property description here:
https://www.chartjs.org/docs/latest/configuration/legend.html
Hope it helps.
I have tried the above approaches on my react(react-chartjs-2) project but no luck. Here I have one different approach like creating custom legends outside the chart. so you can get more control over it
Hide default legend
Get legend object using any ref method.
Loop and make a custom legend using html and css.
Sample codesanbox
I am aware that OP is not about reactjs component, but as it is a common issue it will help someone.
Orginal reference
.
For ng2-charts#^3.1.0, following this answer works with an addition:
this.options.labels.padding = 40;
//this.height += 15;
or the title.padding config (this'll create an invisible title under the graph so it's a bit hacky):
plugins: {
legend: {
display: true,
position: 'bottom',
title: {
display: true,
padding: 10,
},
Stackblitz
After searching the chart.js file I found out that the height of the labels bar is defined there in
height = this._fitRows(titleHeight, fontSize, boxWidth, itemHeight)
OR
this.height = Math.min(height, options.maxHeight || this.maxHeight)
in the fit() function
fit() {
const {options, ctx} = this;
if (!options.display) {
this.width = this.height = 0;
return;
}
const labelOpts = options.labels;
const labelFont = toFont(labelOpts.font);
const fontSize = labelFont.size;
const titleHeight = this._computeTitleHeight();
const {boxWidth, itemHeight} = getBoxSize(labelOpts, fontSize);
let width, height;
ctx.font = labelFont.string;
if (this.isHorizontal()) {
width = this.maxWidth;
height = this._fitRows(titleHeight, fontSize, boxWidth, itemHeight);
} else {
height = this.maxHeight;
width = this._fitCols(titleHeight, fontSize, boxWidth, itemHeight);
}
this.width = Math.min(width, options.maxWidth || this.maxWidth);
this.height = Math.min(height, options.maxHeight || this.maxHeight) + 40;}
And I changed it to +40 as shown above. and it worked fine for me so I wanted to share.
If you want to apply padding below legend for some charts only in your app:
ChartJS >= 2.1.0
Note: make sure to add this in plugins and not inside options.plugins.
Chart.plugins.register({
id: 'paddingBelowLegends',
beforeInit: function(chart, options) {
chart.legend.afterFit = function() {
this.height = this.height + 50;
};
}
});
// ----------------------------------
// disable the plugin only for charts
// where you DO NOT WANT the padding
// ----------------------------------
// for raw ChartJS use:
var chart = new Chart(ctx, {
config: {
plugins: {
paddingBelowLegends: false
}
}
});
For React Users using react-chartjs-2:
import { Line } from "react-chartjs-2";
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend } from "chart.js";
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend);
<Line
data={{
datasets: trendsData?.map((trend, idx) => ({
type: "line",
label: trend.domainName,
data: trend.domainTrends.map(d => d.value),
backgroundColor: getDomainColor(idx).backgroundColor,
borderColor: getDomainColor(idx).color,
pointRadius: 0,
tension: 0.3
})),
labels: trendsData?.[0]?.domainTrends.map(d => d.date)
}}
options={{
plugins: {
legend: {
display: true,
align: "start",
labels: {
font: { size: 14 }
}
}
}
}}
plugins={[
{
id: "increase-legend-spacing",
beforeInit(chart) {
// Get reference to the original fit function
const originalFit = (chart.legend as any).fit;
// Override the fit function
(chart.legend as any).fit = function fit() {
// Call original function and bind scope in order to use `this` correctly inside it
originalFit.bind(chart.legend)();
this.height += 20;
};
}
}
]}
/>
I know this is not what OP really wants but at least for newer version, and the one I use - 4.0.1 - there is no way to increase the margin between the legend box and the chart, that I have been able to find at least, so this is an obvious workaround. So in order to avoid this problem:
I had to change the position of the leyend box below the chart with this option configurations:
options = {
layout: {
padding: 30
},
parsing: {
key: 'nested.value'
},
plugins: {
datalabels: {
color: '#36A2EB'
},
tooltip: {
enabled: true
},
legend: {
position: 'bottom',
align: 'center',
labels: {
padding: 20,
}
}
}
};
Note: To add the values at the top of the bars I had to add the npm package chartjs-plugin-datalabels with the following features inside your datasets config:
datasets: [
{
label: "Ejercido",
data: source.map( s => s.ejercidoTotal),
datalabels: {
anchor: 'end',
clamp: true,//this makes one datalabel not visible if it crashed with other one
display: 'auto',
align: 'top',
color: '#333333',
formatter
}
},
...
]
With this as the end result:
If you are using react-chartjs-2 library to show chart in a React app. You can use the below solution:
const plugin = {
beforeInit: function (chart) {
// Get reference to the original fit function
const originalFit = chart.legend.fit
// Override the fit function
chart.legend.fit = function fit() {
// Bind scope in order to use `this` correctly inside it
originalFit.bind(chart.legend)()
this.height += 20 // Change the height
}
}
}
export const ReactChart2Example = (props) => {
const { data = [] } = props;
return (
<div>
<Chart plugins={[plugin]} type ="bar" data={data}/>
</div>
);
};
We have to pass plugins prop separately in React js Chart component and not inside options.
Reference: https://github.com/chartjs/Chart.js/issues/10388#issuecomment-1217363379
You can use layout property under options. This helped me:
layout: {
padding: {
left: 50,
right: 130,
top: 0,
bottom: 0
}
}
I would like to rotate only firt and last label in x-axis.
I have following formatter function:
formatter() {
if (this.isFirst || this.isLast) {
// First or last label here
var rotatedValue = this.value.doRotation(); // TODO here
return rotatedValue;
} else {
return this.axis.defaultLabelFormatter.call(this);
}
},
I did a research but nothing found (how call some rotation function).
I use a basic line chart.
You can set xAxis.labels.useHTML option to true and style these labels via CSS3:
xAxis: {
labels: {
useHTML: true
}
},
chart: {
events: {
load: function() {
var div = this.xAxis[0].labelGroup.div;
div.firstChild.style.transform = "rotate(30deg)";
div.lastChild.style.transform = "rotate(30deg)";
}
}
}
Live working demo: http://jsfiddle.net/kkulig/fepj1f5g/
API reference: http://api.highcharts.com/highcharts/xAxis.labels.useHTML
// ignore this comment - required to post the following jsfiddle.net link!
Please see https://jsfiddle.net/68bf25vh/
If you click a doughnut segment, the corresponding tooltip displays, which is the correct functionality.
The problem is triggering this desired functionality when a user clicks one of the buttons below the doughnut. E.g. when a user clicks the 'Trigger Segment 1 Click' button. The tooltip should display above segment 1 (just as if the user had clicked segment 1).
A bonus would be having the tooltip displaying above segment 1 initially too, but not essential.
Any help much appreciated :)
Please note
Using Chart.js v 2.5.0. I've read a few articles suggesting to use a showTooltip() method, e.g. chart.showTooltip([chart.segments[0]], true); Unfortunately this method does not exist in this version.
Found this https://stackoverflow.com/a/37989832, but this displays all tooltips. Just want the tooltip of the active (current) segment to display.
You can use the following function to display corresponding tooltip, when clicked on an external button :
function showTooltip(chart, index) {
var segment = chart.getDatasetMeta(0).data[index];
chart.tooltip._active = [segment];
chart.tooltip.update();
chart.draw();
}
When calling the function, pass chart-instance and button-index as the first and second argument respectively.
BONUS :
To initially show the tooltip of segment-1, add the following config in your chart options :
animation: {
onComplete: function() {
if (!isChartRendered) {
showTooltip(myChart, 0);
isChartRendered = true;
}
}
}
* declare a variable named isChartRendered in global-scope and set it to false
ᴡᴏʀᴋɪɴɢ ᴇxᴀᴍᴘʟᴇ ⧩
var isChartRendered = false;
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Segment 1', 'Segment 2', 'Segment 3'],
datasets: [{
data: [10, 10, 10]
}]
},
options: {
events: ['click'],
cutoutPercentage: 70,
legend: {
display: false
},
tooltips: {
displayColors: false
},
onClick: function(evt, elements) {},
// BONUS: show segment 1 tooltip initially
animation: {
onComplete: function() {
if (!isChartRendered) {
showTooltip(myChart, 0);
isChartRendered = true;
}
}
}
}
});
$(document).on('click', 'button', function() {
var $this = $(this),
index = $this.index();
showTooltip(myChart, index);
});
function showTooltip(chart, index) {
var segment = chart.getDatasetMeta(0).data[index];
chart.tooltip._active = [segment];
chart.tooltip.update();
chart.draw();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="width:400px;height:400px;">
<canvas id="myChart"></canvas>
</div>
<div style="margin-top:50px;">
<button>Trigger Segment 1 Click</button>
<button>Trigger Segment 2 Click</button>
<button>Trigger Segment 3 Click</button>
</div>
For Chart.js 3 the GRUNT`s solution needs some modifications:
chart.tooltip.setActiveElements([{datasetIndex: 0, index: index}]);
chart.tooltip.update();
chart.render();
If you want to change also the segment style:
const activeSegment = chart.getDatasetMeta(0).data[index];
chart.updateHoverStyle([{element: activeSegment, datasetIndex: 0}], null, true);
I have a funnel chart that I need to extend through the whole container div when the page gets resized (and becomes thin)
I tried to programmatically hide the label on resize but the chart would still remain in a side of the div
https://jsfiddle.net/MicheleC/9oh3ttss/7/
function toogle_chart_label(chart, series, show){
var opt = chart.series[series].options;
if(typeof show == 'undefined') {
opt.dataLabels.enabled = !opt.dataLabels.enabled;
} else {
opt.dataLabels.enabled = show
}
}
function funnel_labels_adjust(){
if($('#container').width() < 300){
toogle_chart_label(funnel_chart, 0, false)
} else {
toogle_chart_label(funnel_chart, 0, true)
}
}
$( window ).resize(function() {
funnel_labels_adjust()
});
In Highcharts 5 there is a new property responsive. It allows you to specify the chart options which will be applied under some conditions.
In your case, you want to to disable data labels when the chart has width below 300.
responsive: {
rules: [{
condition: {
maxWidth: 300
},
chartOptions: {
plotOptions: {
funnel: {
dataLabels: {
enabled: false
}
}
}
}
}]
}
example: https://jsfiddle.net/9oh3ttss/8/
If you dont want to use that feature, you need to use dynamic methods like series.update()
series[0].update({
dataLabels: {enabled: false / true}
});
Hi here I set the data to the bar chart:
setDatosBarra: function(data){ //
var self = this;
var horaEstim = 0;
var horaReal = 0;
var horaTotal = 0;
if(data[0].horas_estim != '-'){
horaEstim = data[0].horas_estim;
}
if(data[0].horas_real != '-'){
horaReal = data[0].horas_real;
}
if(data[0].total_horas != '-'){
horaTotal = data[0].total_horas;
}
var datosBarra =[{data: [[0,horaEstim]], color: "#691717"}, {data: [[1,horaReal]], color: "#173D69"},{data: [[2,horaTotal]], color: "#176469"}];
self.flotLinea(datosBarra);
},
When all is ready I send the data to self.flotBar;
This is the flotBar function:
flotBar: function(datos){
var self = this;
if(datos == 0){
var data = [["SIN REGISTROS",0]];
}else{
var data = datos;
}
function getTooltip(label, x, y) {
return "<strong style='font-size:18px;'> " + y + " </strong> horas";
}
var plot = $.plot("#placeholder",data, {
series: {
bars: {
show: true,
barWidth: 0.3,
align: "center",
lineWidth: 0,
fill:.75
}
},
xaxis: {
ticks: [[0,"Horas estimadas"],[1,"Horas reales"],[2,"Total horas"]],
mode: "categories",
tickLength: 0
},
grid: {
hoverable: true,
clickable: true
},
tooltip: true,
tooltipOpts : {
content : getTooltip,
defaultTheme : false
},
});
},
Ok , and this is my problem, example:
I select a option in an dropDown:
And the bar chart looks like this:
If I select other option in the dropDown:
The bar chart looks like this:
And if I select again the first option "Correcion de errores", the bar chart looks like this:
So.. always the first time that I show the bar chart looks like in the first image , with the numbers in the line, but If I select other option looks good.
I need see good the bar chart always and no just when I select other option.
I'm using flot javascript library.
How can I fix this? sorry by my english
The main issue with the question as stated is that we do not have all the code. In essence, you should either provide all the code, or shrink down the problem to something that shows the issue and then, well, provide all the code. As far as I can guess, you have some other code somewhere else that is drawing the initial chart. The second and subsequent times? Drawn properly. To support my assertion, notice that in your initial image the captions for the x-axis tick markers (ditto the bars themselves) are right aligned not centered.
For fun, I wrote a quick jsFiddle that showed how to switch datasets using a button (much as you want to do with the drop-down) and redraw the chart:
drawChart = function(index) {
var chartData = getDataForChart(rawData[index]);
if (chart) {
chart.setData(chartData);
chart.draw();
}
else {
chart = $.plot("#barchart", chartData, chartOptions);
}
},
switchDataset = function() {
datasetIndex = (datasetIndex + 1) % datasetCount;
drawChart(datasetIndex);
};
$("#switchButton").on("click", switchDataset);
Because I decided to load new data into the chart rather than redraw it all from scratch (to be honest I saw no real difference either way), it meant that I had to pre-calculate the maximum value for the y-axis:
calcValueMax = function() {
var max = 0;
rawData.forEach(function(values) {
values.forEach(function(value) {
if (value > max) {
max = value;
}
});
});
return max;
},
// other code
chartOptions.yaxis.max = calcValueMax();
Hope that helps.