I'm trying to create a simple doughnut chart with chartjs. I've been digging through the Documentation and Stack (of course), but I couldn't find it.
Here is my code: https://jsfiddle.net/zyqtyna7/1/
<div class="place-for-chart">
<canvas id="myChart"></canvas>
</div>
<div class="description">
<p class="first hide">I'm description to no. 1 and I was hide</p>
<p class="second hide">I'm description to no. 2 and I was hide</p>
<p class="third hide">I'm description to no. 3 and I was hide</p>
</div>
<script>
var data = {
datasets: [{
data: [20, 20, 20],
backgroundColor: ["#27ae60", "#95a5a6", "#488a99"]
}],
labels: ["first", "second", "third"],
};
$(document).ready(
function() {
var canvas = document.getElementById("myChart");
var ctx = canvas.getContext("2d");
var CompetenceChart = new Chart(ctx, {
type: 'doughnut',
data: data
});
})
I'm not sure, but I think that the biggest question is: how can I target a specific segment of a chart (and then do something with it)? I'm asking this, because my project requires that:
descriptions in paragraphs will be visible after user's click at related part of chart (how can I target this segment???);
tooltips will have only labels' name (no values) (I couldn't decipher the Documentation);
chart animation should be triggered with scroll (segments will appear in sequence after scrolling - is it even possible?)
I'll be very greatfull for any insight - I'm stuck!
I managed to do 2 of 3.. and I found documentation on how to do the third one.. But I was not able to make it work :/.... (i will try again when i have a bit more of time).
So here is the JSfiddle with this modifications:
1:Data is shown on mouse click
2:On top labels are shown, but when you click you wont see any labels but a black mark
JSFIDDLE
So what did I do?
options: {
// This chart will not respond to mousemove, etc
events: ['click'],
tooltips: {
callbacks: {
label: function(tooltipItem)
{
return tooltipItem.yLabel;
}
}
}
}
I added the options part with those codes.
the events:['click'] makes the labels show when you click a part of the chart instead of hovering it.
the tooltips with the callbacks "turn off" showing the labels on mouse click.
And for the other part that you ask, about showing the animation when you scroll to the part where the chart is I found this 2 links that tells you how to do so (I couldn't make it work, but I will try again when I have more time and update).
Link1
Link2
Please let me know if this is what you wanted to know! CHEERS!
OMG! Now I know. It took me so many hours, but was so obvious! Here's my new - working - code: https://jsfiddle.net/m954jto4/ Documentation of Chartjs and same basic js script - that's all, what I needed (shame on me!).
<div class="place-for-chart">
<canvas id="myChart"></canvas>
</div>
<div class="description">
<p class="first hidden" id="hide1">I'm description to no. 1 and I was hide</p>
<p class="first hidden" id="hide2">I'm description to no. 2 and I was hide</p>
<p class="first hidden" id="hide3">I'm description to no. 3 and I was hide</p>
</div>
var data = {
datasets: [{
data: [20, 20, 20],
backgroundColor: ["#27ae60", "#95a5a6", "#488a99"]
}],
labels: ["first", "second", "third"],
};
$(document).ready(
function() {
var canvas = document.getElementById("myChart");
var ctx = canvas.getContext("2d");
var myNewChart = new Chart(ctx, {
type: 'doughnut',
data: data,
options: {
legend: {
display: true,
onClick: (e) => e.stopPropagation(),
position: 'left',
labels: {
fontSize: 20
}
},
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var label = data.labels[tooltipItem.index];
return label;
}
}
},
cutoutPercentage: 65
}
}
);
canvas.onclick = function(event) {
var activePoints = myNewChart.getElementsAtEvent(event);
var hiddenparagraph1 = document.getElementById("hide1");
var hiddenparagraph2 = document.getElementById("hide2");
var hiddenparagraph3 = document.getElementById("hide3");
if (activePoints.length > 0) {
var clickedSegmentIndex = activePoints[0]._index;
if (clickedSegmentIndex==0) {
hiddenparagraph1.classList.remove("hidden");
}
else if(clickedSegmentIndex==1) {
hiddenparagraph2.classList.remove("hidden");
}
else {
hiddenparagraph3.classList.remove("hidden");
}
}
};
}
);
Related
Before I explain my problem, I just wanna apologize for the wording of the question, I didnt know how to word it properly.
Here's my problem. I want to display a chartJS about the evolution of the quantity of a product, by week, month & year. The issue is : I have six products like that. The only solution I have in my mind, is destroying a graph and re-creating one for each possibility. Which... is I bet terrible. To choose what product to show, I have created a SelectBox where you choose the product. From there, I'll like to display the corresponding chartJS. The only solution I have found "possible" was using RadioButtons and then verifying the different combinations with a switchcase.
Does someone has a more feasible idea to fix this concept's issue ? Thanks a lot in advance and I apologize for my broken english.
EDIT : AS asked by #AndrewL64, here is an example of my code used to change the type of the graph after a click on a button. It is not exactly what I want to do for the final product, but it shows easily the issue I am running into. It is a total mess. And this is only for ONE product and three types of graph. I've read about ChartJS that you cant update its type and you have to recreate it.
function changeGraphType(type){
var gradctx = document.getElementById('myChart').getContext("2d");
let gradient = gradctx.createLinearGradient(0, 0, 0, 600);
var ctx = document.getElementById('myChart');
//gradient bar & line
gradient.addColorStop(0, 'orange');
gradient.addColorStop(1, 'purple');
graphique.destroy();
if(type==='pie'){
graphique = new Chart(ctx.getContext('2d'), {
type: type,
data: {
labels: labels,
datasets: [{
label: 'Ceci est un test',
data: donnee,
backgroundColor: gradient, //['#FF3254','#85ff2d','#ffdb0f','#FF3254','#FF3254','#FF3254','#FF3254','#FF3254','#FF3254'],
hoverBackgroundColor:'rgb(252,10,19)'
}],
},
});
}
else if(type==='line'){
graphique = new Chart(ctx.getContext('2d'), {
type: type,
data: {
labels: labels,
datasets: [{
label: 'Grillage blindax',
data: donnee,
backgroundColor:gradient,
}],
},
});
}
else{
graphique = new Chart(ctx.getContext('2d'), {
type: type,
data: {
labels: labels,
datasets: [{
label: 'Test',
data: donnee,
backgroundColor:gradient,
hoverBackgroundColor:'rgb(64,252,52)'
}],
},
});
}
}
Just change the select and the box will be changed too.
if you need more boxes just add a new div with class box and some id but note you have to add option too and the value must be the same id of the box to relate to each ohter.
I hope that can help.
<div class="box active" id="chartJS-1">Box 1 is here</div>
<div class="box" id="chartJS-2">Box 2 is here</div>
<div class="box" id="chartJS-3">Box 3 is here</div>
<select id="select">
<option value="chartJS-1" selected>chartJS-1</option>
<option value="chartJS-2">chartJS-2</option>
<option value="chartJS-3">chartJS-3</option>
</select>
.box {
height: 100px;
background: rgb(121, 3, 105);
color: #fff;
text-align: center;
font-size: 50px;
font-family: 'Courier New', Courier, monospace;
display: none;
}
.box.active {
display: block;
}
const select = document.querySelector('#select');
const boxes = document.querySelectorAll('.box');
select.onchange = _ => {
let value = select.value;
boxes.forEach(box => box.classList.remove('active'));
document.querySelector(`#${value}`).classList.add('active');
};
In a chart I render using Plotly.js, I define titles for each axis. When mouse hovering the items within the chart, a popup is shown, but the "labels" shown do not use the titles I had defined.
For example, the default value for the x axis is x. I defined hellox as title and that value is show in the chart, but not when mouse hovering a value (x is still shown).
See a live example here of what I mean: https://codepen.io/anon/pen/qoGQvx
I've been looking a the documentation and I didn't find anything so far that did exactly what I wanted: simply change the labels in the popup.
Also the question is quite old, I would like to write a solution I came up when facing the same problem. I did define a var text array for hover info which I filled with the labels for x, y and z values. Please have a look at the following fiddle where I use a heatmap plot for demonstration (this is what I am using in my project, but it can be easily adapted for your chart option): http://jsfiddle.net/zLc5y63g/
This is the html code:
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<body>
<br><br>
<!-- Source and on-click event for plotly.js -->
<div id="plotly_chart" style="width: 90%; height: 270px"></div>
And this the JavaScript:
var zValues = [[1, 20, 30], [20, 1, 60], [30, 60, 1]];
var yValues = ['data1', 'data2', 'data3'];
var xValues = ['condition1', 'condition2', 'condition3'];
var config = {
displaylogo: false
};
// fill in 'text' array for hover
var text = zValues.map (function(zValues, i) { return zValues.map (function (value, j) {
return ` ID: ${yValues[i]}<br> Condition: ${xValues[j]}<br> Value: ${value.toFixed(2)} `
});
});
Plotly.newPlot('plotly_chart', [{x: xValues, y: yValues, z: zValues, text: text, hoverinfo: 'text', hoverlabel: {bgcolor: '#41454c'}, type: 'heatmap', colorscale: 'Viridis'}], config );
Maybe this is still useful.
// 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 bar chart in chart.js 2.1.2 which I just upgraded from 1.something. In 1.something, you could specify when to animate the bar chart. Essentially, I load the chart on page load and then set a timer to update the data and redraw the chart every 5 seconds without requiring the user to reload the page. I would like for the bar chart to only animate on the initial load and not in subsequent refreshes. In version 1.something you just needed to change the animation property of the bar chart when refreshing it. In version 2.1.2, I don't see a way to do that. At this point, I would be happy if I could disable the animation of the bar chart entirely. However, I also have other pie charts on the page which the animation settings are working so I don't want to change the animation settings globally.
HTML (Excerpt)
<div class="text-center">
<div class="row">
<div id="regbyhour-container" class="col-sm-12">
<h4>Registrations by Hour</h4>
<canvas id="regbyhour"></canvas>
</div>
</div>
</div>
Javascript
<script type="text/javascript">
var barData = defineBarDataArray();
$(function () {
// Global Chart Options
Chart.defaults.global.legend.display = false;
Chart.defaults.global.maintainAspectRatio = true;
// Bar Chart Options
Chart.defaults.bar.scaleBeginAtZero = false;
updateBarChart(true)
setInterval(function () {
updateBarChart(false)
}, 5000);
});
function defineBarDataArray() {
return {
labels: [],
datasets: [{
label: "Registrations",
backgroundColor: "rgba(151,187,205,0.5)",
borderColor: "rgba(151,187,205,0.8)",
borderWidth: 3,
data: []
}]
};
};
function drawRegByHourChart(animate) {
$("#regbyhour").remove();
$("#regbyhour-container").append('<canvas id="regbyhour"></canvas>');
var context = $("#regbyhour");
var chart = new Chart(context, {
type: 'bar',
data: barData
// Need to enable or disable animation here based on animate parameter
});
};
function updateBarChart(animate) {
barData = defineBarDataArray();
barData.labels.push("12:00 PM");
barData.labels.push("1:00 PM");
barData.labels.push("2:00 PM");
barData.datasets[0].data.push(1 + Math.floor(Math.random() * 2000));
barData.datasets[0].data.push(1 + Math.floor(Math.random() * 2000));
barData.datasets[0].data.push(1 + Math.floor(Math.random() * 2000));
drawRegByHourChart(animate);
};
</script>
I don't see anything in the documentation here that says you can specify the animation options like you can with a pie chart. What am I missing?
I ended up posting this as a bug on the chart.js GitHub page since I didn't get any responses here. Following the suggestion from etimberg I changed the drawRegByHourChart function from this:
function drawRegByHourChart(animate) {
$("#regbyhour").remove();
$("#regbyhour-container").append('<canvas id="regbyhour"></canvas>');
var context = $("#regbyhour");
var chart = new Chart(context, {
type: 'bar',
data: barData
// Need to enable or disable animation here based on animate parameter
});
};
To this:
function drawRegByHourChart(animate) {
$("#regbyhour").remove();
$("#regbyhour-container").append('<canvas id="regbyhour"></canvas>');
var context = $("#regbyhour");
var chart = new Chart(context, {
type: 'bar',
data: barData
});
if (!animate) {
chart.update(0);
}
};
I'm trying to use ChartsJS within a tabbing system. The first chart in the first tab is rendered, but subsequent charts are not.
I believe this is because the tabs have display:none, so when the charts are first generated they are created in a div with zero dimensions.
This fixes the issue, but breaks the tabs:
.vertical-nav>div.tab-content {
display: block !important;
}
With that in mind I've tried all kinds of ways to regenerate the charts after the tab is open, or force display:block just before generating the chart. Nothing works.
Here's my attempt to regenerate the charts:
jQuery(document).ready(function() {
jQuery('.vertical-nav ul li span').click(function(){
// Target the canvas within the correct tab
var tabIndex = jQuery(this).parent().index();
var canvasID = jQuery('.vertical-nav .tab-content:eq( ' + tabIndex + ' ) canvas').attr('id');
theChart = charts[canvasID].chart;
builtChart = charts[canvasID].builtChart;
// Destroy all of the charts so that we can rebuild them
for (var key in charts) {
if (charts.hasOwnProperty(key)) {
charts[key].builtChart.destroy();
}
}
// get the chart data and options from the charts object
newData = charts[canvasID].data;
newOptions = charts[canvasID].options;
// create the new chart
newChart = document.getElementById(canvasID).getContext("2d");
new Chart(newChart).Line(newData, newOptions);
});
})
The staging site is: http://telcomplusplc.uberleaf.com/company/?tab=our-growth
I've even tried setting a timeout to delay the chart generation until the tab is displayed, but no luck.
You can see in the URL above that the first chart generates and regenerates as it should. It's just the rest of them that don't (presumably because they are with a display:none div on first generation).
Any help is much appreciated. Happy to give more info if needed.
Update
When the window is resized, the chart is redrawn. Here's the code for that (in chart.js):
// Attach global event to resize each chart instance when the browser resizes
helpers.addEvent(window, "resize", (function(){
// Basic debounce of resize function so it doesn't hurt performance when resizing browser.
var timeout;
return function(){
clearTimeout(timeout);
timeout = setTimeout(function(){
each(Chart.instances,function(instance){
// If the responsive flag is set in the chart instance config
// Cascade the resize event down to the chart.
if (instance.options.responsive){
instance.resize(instance.render, true);
}
});
}, 50);
};
})());
I think what I need is to be able to fire that from a click function. I've tried an failed, my JS skills aren't that good.
If you use display:none then chartjs doesn't know the size to make the chart and then doesn't. If you switch to jquery.hide()/show() it will work. Here's a jsfiddle
*The fiddle works without it but if you have a lot of content then you may need to also hide the area temporarily.
Javascript
var data1 = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{ data: [65, 59, 80, 81, 56, 55, 40] }]
};
var data2 = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{ data: [65, 59, 80, 81, 56, 55, 40] }]
};
// Load each chart and hide() the tab immediately.
var ctx = document.getElementById('chart1').getContext('2d');
var chart1 = new Chart(ctx).Bar(data1);
$('#tab1').hide();
var ctx = document.getElementById('chart2').getContext('2d');
var chart2 = new Chart(ctx).Line(data2);
$('#tab2').hide();
/*
// will not work because chart is not rendered
$('#tab1_btn').on('click',function(){
$('#tab1').css('display','block');
$('#tab2').css('display','none') })
$('#tab2_btn').on('click',function(){
$('#tab1').css('display','none');
$('#tab2').css('display','block') })
*/
$('#tab1_btn').on('click',function(){
$('#tab1').show();
$('#tab2').hide()
})
$('#tab2_btn').on('click',function(){
$('#tab1').hide();
$('#tab2').show()
})
// if you have a lot of content in multiple tabs
// it may be necessary to temporarily cover the content
$('#tab_cover').hide();
HTML
<button id="tab1_btn">tab1</button>
<button id="tab2_btn">tab2</button>
<div id="tab_cover"> </div>
<div id="tabs">
<div id="tab1" class="tab">
<canvas id="chart1" class="chart" width="400" height="300"></canvas>
</div>
<div id="tab2" class="tab">
<canvas id="chart2" class="chart" width="400" height="300"></canvas>
</div>
</div>
CSS
/* will not work because chart is not rendered
.tab { display:none } */
.chart { width:50% }
.float { float:right; width:50%; }
#tab_cover { background:#9ff; height: 100% !important; width:100%; position: absolute; z-index: 300; }
I've found a solution. I was close with the code I had in the question. It seems that I need to destroy and resize the charts.
I also had to change the display status of the tabs manually.
Here's what works for me:
function createNewChart(canvasID) {
// get the chart data and options from the charts object
newData = charts[canvasID].data;
newOptions = charts[canvasID].options;
// create the new chart
theChart = document.getElementById(canvasID).getContext("2d"); console.dir(theChart);
new Chart(theChart).Line(newData, newOptions);
}
jQuery('.vertical-nav ul li').click(function(){
// Target the canvas within the correct tab
var tabIndex = jQuery(this).index();
var canvasID = jQuery('.vertical-nav .tab-content:eq( ' + tabIndex + ' ) canvas').attr('id');
theChart = charts[canvasID].chart;
builtChart = charts[canvasID].builtChart;
// Destroy all of the charts so that we can rebuild them
for (var key in charts) {
if (charts.hasOwnProperty(key)) {
jQuery('.vertical-nav .tab-content:eq( ' + tabIndex + ' )').css('display', 'block');
jQuery('.vertical-nav .tab-content:eq( ' + tabIndex + ' ) .tab-content-chart').css('display', 'block');
charts[key].builtChart.destroy();
charts[key].builtChart.resize();
}
}
createNewChart(canvasID);
});
I was running into the same issues when using hidden divs that only display upon certain selections from a drop-down. What I found to work is similar to what ow3n did. I created the graphs and then had them only generate once the selection was made.
HTML:
<div>
<select>
<option value="option-1">Option 1</option>
<option value="option-2">Option 2</option>
<option value="option-3">Option 3</option>
</select>
</div>
<div class="chart1">
<canvas id="chart1"></canvas>
</div>
<div class="chart2">
<canvas id="chart2"></canvas>
</div>
<div class="chart3">
<canvas id="chart3"></canvas>
</div>
Javascript:
//example of chart variables (I only included 1 example)
var chart1 = {
labels : ["2009","2010","2011","2012","2013","2014"],
labelAlign: 'center',
datasets : [
{
label: "Visitor Average",
fillColor : "rgba(255,255,255,0)",
strokeColor : "rgba(0,34,221,1)",
pointColor : "rgba(0,34,221,1)",
pointStrokeColor : "#fff",
pointHighlightFill : "#fff",
pointHighlightStroke : "rgba(0,34,221,1)",
data: [28, 48, 40, 19, 86, 27]
},
{
label: "Baseline Average",
fillColor : "rgba(255,255,255,0)",
strokeColor : "rgba(255,0,0,1)",
pointColor : "rgba(255,0,0,1)",
pointStrokeColor : "#fff",
pointHighlightFill : "#fff",
pointHighlightStroke : "rgba(255,0,0,1)",
data: [65, 59, 80, 81, 56, 55]
}
]
}
window.onload = function(){
var ctx1 = document.getElementById("chart1").getContext("2d");
window.myLine1 = new Chart(ctx1).Line(chart1, {
responsive: true,
});
}
//change graph per drop-down selection
$(document).ready(function(){
$("select").change(function(){
$( "select option:selected").each(function(){
if($(this).attr("value")=="option-1"){
$(".chart2").hide();
$(".chart3").hide();
$(".chart1").show();
var ctx1 = document.getElementById("chart1").getContext("2d");
window.myLine1 = new Chart(ctx1).Line(chart1, {
responsive: true,
});
}
if($(this).attr("value")=="option-2"){
$(".chart2").show();
$(".chart1").hide();
$(".chart3").hide();
var ctx2 = document.getElementById("chart2").getContext("2d");
window.myLine2 = new Chart(ctx2).Line(chart2, {
responsive: true,
});
}
if($(this).attr("value")=="option-3"){
$(".chart3").show();
$(".chart1").hide();
$(".chart2").hide();
var ctx3 = document.getElementById("chart3").getContext("2d");
window.myLine3 = new Chart(ctx3).Line(chart3, {
responsive: true,
});
}
});
}).change();
});
Taking bootstrap's tab as an example, my solution is to add class .loading-data to .tab-content before rendering using chart.js and remove the class after the rendering is done.
.tab-content.loading-data .tab-pane {
display: block;
}
In this way, the .tab-panes will display in the instant of chartjs's rendering.
Try this one, I struggled with all other approaches but finally done with this code.
$("a").click(function(e){
let val = '#'+ e.target.href.split("#")[1]
localStorage.setItem('tab', val)
window.location.reload();
})
$(document).ready(function()
{
$('a[href="' + localStorage.getItem('tab') + '"]').parent().addClass('active')
$(document.getElementById(localStorage.getItem('tab').slice(1))).addClass('active')
});
Also, try with these approches
window.dispatchEvent(new Event('resize'))
Chartkick.charts["<id of chart element like chart-1>"].redraw()