Can someone tell me what I'm doing wrong here, chart renders but when I click the bar I get error in console:
Uncaught TypeError: ComboChart.getElementsAtEvent is not a function
at HTMLCanvasElement.document.getElementById.onclick.
I verified a couple of other posts on stack but don't seem to get an idea, I'm fairly new to javascript, please help me understand why this wouldn't work.
Thanks a lot!
<head>
<link href="https://fonts.googleapis.com/css?family=Quicksand" rel="stylesheet">
<style>
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
grid-auto-rows: minmax(100px, auto);
grid-row-gap: 1em;
grid-column-gap: 1em;
justify-items: stretch;
align-items: stretch;
grid-auto-flow: column;
}
.wrapper>div {
//background: #eee;
padding: 1em;
}
.wrapper>div:nth-child(odd) {
//background: #ddd;
}
.ComboChart {
/*justify-self:end;*/
grid-column: 1/9;
grid-row: 1/5;
}
.container {
/*align-self:end;*/
grid-column: 7/9;
grid-row: 2;
}
#myProgress {
width: 100%;
background-color: #ddd;
}
#myBar {
width: N/A%;
height: 30px;
background-color: ;
text-align: center;
line-height: 30px;
color: white;
}
.Top5 {
/*align-self:end;*/
grid-column: 1/3;
grid-row: 3;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="ComboChart">
<canvas id="ComboChart"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"></script>
<script>
console.log(Chart.defaults.global)
var obj = JSON.parse('{"Account":{"JAN":{"Month":"JAN","MonthlyBudget":323342.6200000000,"MonthlyPosted":207198.1700000000,"MonthlyVariance":116144.4500000000,"YTDBudget":323342.6200000000,"YTDPosted":207198.1700000000,"YTDVariance":116144.4500000000,"AnnualBudget":3879559.340000000,"AnnualPosted":1935521.880000000,"AnnualVariance":1944037.460000000},"FEB":{"Month":"FEB","MonthlyBudget":303967.7900000000,"MonthlyPosted":333011.4200000000,"MonthlyVariance":-29043.63000000000,"YTDBudget":627310.4100000000,"YTDPosted":540092.5900000000,"YTDVariance":87217.82000000002,"AnnualBudget":3879559.340000000,"AnnualPosted":1935613.380000000,"AnnualVariance":1943945.960000000},"MAR":{"Month":"MAR","MonthlyBudget":325389.6400000000,"MonthlyPosted":223266.5100000000,"MonthlyVariance":102123.1300000000,"YTDBudget":952700.0500000000,"YTDPosted":761933.8800000000,"YTDVariance":190766.1700000000,"AnnualBudget":3879559.340000000,"AnnualPosted":1929419.560000000,"AnnualVariance":1950139.780000000},"APR":{"Month":"APR","MonthlyBudget":321608.2000000000,"MonthlyPosted":268868.0300000001,"MonthlyVariance":52740.17000000000,"YTDBudget":1274308.250000000,"YTDPosted":1032227.130000000,"YTDVariance":242081.1200000000,"AnnualBudget":3879559.340000000,"AnnualPosted":1935613.380000000,"AnnualVariance":1943945.960000000},"MAY":{"Month":"MAY","MonthlyBudget":328223.9600000000,"MonthlyPosted":267917.2600000000,"MonthlyVariance":60306.70000000000,"YTDBudget":1602532.210000000,"YTDPosted":1300144.390000000,"YTDVariance":302387.8200000000,"AnnualBudget":3879559.340000000,"AnnualPosted":1937114.100000000,"AnnualVariance":1942445.240000000},"JUN":{"Month":"JUN","MonthlyBudget":321608.2000000000,"MonthlyPosted":256167.9500000000,"MonthlyVariance":65440.25000000000,"YTDBudget":1924140.410000000,"YTDPosted":1555711.980000000,"YTDVariance":368428.4300000000,"AnnualBudget":3879559.340000000,"AnnualPosted":1935613.380000000,"AnnualVariance":1943945.960000000},"JUL":{"Month":"JUL","MonthlyBudget":328223.9600000000,"MonthlyPosted":268862.8300000000,"MonthlyVariance":59361.13000000000,"YTDBudget":2252364.370000000,"YTDPosted":1821052.940000000,"YTDVariance":431311.4300000000,"AnnualBudget":3879559.340000000,"AnnualPosted":1930920.280000000,"AnnualVariance":1948639.060000000},"AUG":{"Month":"AUG","MonthlyBudget":328223.9600000000,"MonthlyPosted":250416.9300000000,"MonthlyVariance":77807.03000000000,"YTDBudget":2580588.330000000,"YTDPosted":2075383.600000000,"YTDVariance":505204.7300000000,"AnnualBudget":3879559.340000000,"AnnualPosted":1936905.600000000,"AnnualVariance":1942653.740000000},"SEP":{"Month":"SEP","MonthlyBudget":313274.8700000000,"MonthlyPosted":-76566.64000000000,"MonthlyVariance":389841.5100000000,"YTDBudget":2827196.560000000,"YTDPosted":1961361.240000000,"YTDVariance":865835.3200000000,"AnnualBudget":3779559.380000000,"AnnualPosted":1900049.880000000,"AnnualVariance":1879509.500000000},"OCT":{"Month":"OCT","MonthlyBudget":0,"MonthlyPosted":-61311.36000000000,"MonthlyVariance":61311.36000000000,"YTDBudget":1764331.340000000,"YTDPosted":1231178.370000000,"YTDVariance":533152.9700000000,"AnnualBudget":2362354.180000000,"AnnualPosted":1231178.370000000,"AnnualVariance":1131175.810000000}}}');
if (obj.Account.NOV != null) {
novMonth = obj.Account.NOV.Month;
novAnnualPosted = obj.Account.NOV.AnnualPosted;
novAnnualVariance = obj.Account.NOV.AnnualVariance;
} else {
novMonth = 'NOV';
novAnnualPosted = 0;
novAnnualVariance = 0;
}
if (obj.Account.DEC != null) {
decMonth = obj.Account.DEC.Month;
decAnnualPosted = obj.Account.DEC.AnnualPosted;
decAnnualVariance = obj.Account.DEC.AnnualVariance;
} else {
decMonth = 'DEC';
decAnnualPosted = 0;
decAnnualVariance = 0;
}
var comma = ",";
var month = novMonth.concat(decMonth);
var randomColorFactor = function() {
return Math.round(Math.random() * 255);
};
var randomColor = function(opacity) {
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
};
new Chart(document.getElementById("ComboChart"), {
type: 'bar',
data: {
labels: [obj.Account.JAN.Month, obj.Account.FEB.Month, obj.Account.MAR.Month, obj.Account.APR.Month, obj.Account.MAY.Month, obj.Account.JUN.Month, obj.Account.JUL.Month, obj.Account.AUG.Month, obj.Account.SEP.Month, obj.Account.OCT.Month, month],
datasets: [{
label: "Annual Posted",
type: "bar",
borderColor: 'rgb(115, 115, 115)',
backgroundColor: 'rgb(115, 115, 115)',
data: [obj.Account.JAN.AnnualPosted,
obj.Account.FEB.AnnualPosted,
obj.Account.MAR.AnnualPosted,
obj.Account.APR.AnnualPosted,
obj.Account.MAY.AnnualPosted,
obj.Account.JUN.AnnualPosted,
obj.Account.JUL.AnnualPosted,
obj.Account.AUG.AnnualPosted,
obj.Account.SEP.AnnualPosted,
obj.Account.OCT.AnnualPosted,
novAnnualPosted,
decAnnualPosted
],
fill: false
},
{
label: "Annual Variance",
type: "bar",
borderColor: 'rgb(210, 11, 11)',
backgroundColor: 'rgb(210, 11, 11)',
data: [obj.Account.JAN.AnnualVariance,
obj.Account.FEB.AnnualVariance,
obj.Account.MAR.AnnualVariance,
obj.Account.APR.AnnualVariance,
obj.Account.MAY.AnnualVariance,
obj.Account.JUN.AnnualVariance,
obj.Account.JUL.AnnualVariance,
obj.Account.AUG.AnnualVariance,
obj.Account.SEP.AnnualVariance,
obj.Account.OCT.AnnualVariance,
novAnnualVariance,
decAnnualVariance
],
fill: false
}
]
},
options: {
title: {
display: true,
text: 'YTD Budget vs Posted',
position: 'right'
},
legend: {
display: true,
position: 'bottom'
},
maintainAspectRatio: false,
responsive: true,
responsiveAnimationDuration: 1500,
scales: {
xAxes: [{
gridLines: {
display: false
}
}],
yAxes: [{
gridLines: {
display: false
},
ticks: {
beginAtZero: true,
callback: function(value, index, values) {
if (parseInt(value) >= 1000) {
return '$' + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
} else {
return '$' + value;
}
}
}
}]
},
}
});
document.getElementById("ComboChart").onclick = function(evt) {
var activePoints = ComboChart.getElementsAtEvent(evt);
var firstPoint = activePoints[0];
var label = ComboChart.data.labels[firstPoint._index];
var value = ComboChart.data.datasets[firstPoint._datasetIndex].data[firstPoint._index];
if (firstPoint !== undefined)
alert(label + ": " + value);
};
</script>
</div>
</div>
</body>
<body>
You will have to instantiate ComboChart in your code.
When you create the chart using new Chart(document.getElementById("ComboChart")), you will have to assign the result to a variable called ComboChart: var ComboChart = new Chart(document.getElementById("ComboChart"))
That way, you can reference it from the event handler.
<head>
<link href="https://fonts.googleapis.com/css?family=Quicksand" rel="stylesheet">
<style>
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
grid-auto-rows: minmax(100px, auto);
grid-row-gap: 1em;
grid-column-gap: 1em;
justify-items: stretch;
align-items: stretch;
grid-auto-flow: column;
}
.wrapper>div {
//background: #eee;
padding: 1em;
}
.wrapper>div:nth-child(odd) {
//background: #ddd;
}
.ComboChart {
/*justify-self:end;*/
grid-column: 1/9;
grid-row: 1/5;
}
.container {
/*align-self:end;*/
grid-column: 7/9;
grid-row: 2;
}
#myProgress {
width: 100%;
background-color: #ddd;
}
#myBar {
width: N/A%;
height: 30px;
background-color: ;
text-align: center;
line-height: 30px;
color: white;
}
.Top5 {
/*align-self:end;*/
grid-column: 1/3;
grid-row: 3;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="ComboChart">
<canvas id="ComboChart"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"></script>
<script>
console.log(Chart.defaults.global)
var obj = JSON.parse('{"Account":{"JAN":{"Month":"JAN","MonthlyBudget":323342.6200000000,"MonthlyPosted":207198.1700000000,"MonthlyVariance":116144.4500000000,"YTDBudget":323342.6200000000,"YTDPosted":207198.1700000000,"YTDVariance":116144.4500000000,"AnnualBudget":3879559.340000000,"AnnualPosted":1935521.880000000,"AnnualVariance":1944037.460000000},"FEB":{"Month":"FEB","MonthlyBudget":303967.7900000000,"MonthlyPosted":333011.4200000000,"MonthlyVariance":-29043.63000000000,"YTDBudget":627310.4100000000,"YTDPosted":540092.5900000000,"YTDVariance":87217.82000000002,"AnnualBudget":3879559.340000000,"AnnualPosted":1935613.380000000,"AnnualVariance":1943945.960000000},"MAR":{"Month":"MAR","MonthlyBudget":325389.6400000000,"MonthlyPosted":223266.5100000000,"MonthlyVariance":102123.1300000000,"YTDBudget":952700.0500000000,"YTDPosted":761933.8800000000,"YTDVariance":190766.1700000000,"AnnualBudget":3879559.340000000,"AnnualPosted":1929419.560000000,"AnnualVariance":1950139.780000000},"APR":{"Month":"APR","MonthlyBudget":321608.2000000000,"MonthlyPosted":268868.0300000001,"MonthlyVariance":52740.17000000000,"YTDBudget":1274308.250000000,"YTDPosted":1032227.130000000,"YTDVariance":242081.1200000000,"AnnualBudget":3879559.340000000,"AnnualPosted":1935613.380000000,"AnnualVariance":1943945.960000000},"MAY":{"Month":"MAY","MonthlyBudget":328223.9600000000,"MonthlyPosted":267917.2600000000,"MonthlyVariance":60306.70000000000,"YTDBudget":1602532.210000000,"YTDPosted":1300144.390000000,"YTDVariance":302387.8200000000,"AnnualBudget":3879559.340000000,"AnnualPosted":1937114.100000000,"AnnualVariance":1942445.240000000},"JUN":{"Month":"JUN","MonthlyBudget":321608.2000000000,"MonthlyPosted":256167.9500000000,"MonthlyVariance":65440.25000000000,"YTDBudget":1924140.410000000,"YTDPosted":1555711.980000000,"YTDVariance":368428.4300000000,"AnnualBudget":3879559.340000000,"AnnualPosted":1935613.380000000,"AnnualVariance":1943945.960000000},"JUL":{"Month":"JUL","MonthlyBudget":328223.9600000000,"MonthlyPosted":268862.8300000000,"MonthlyVariance":59361.13000000000,"YTDBudget":2252364.370000000,"YTDPosted":1821052.940000000,"YTDVariance":431311.4300000000,"AnnualBudget":3879559.340000000,"AnnualPosted":1930920.280000000,"AnnualVariance":1948639.060000000},"AUG":{"Month":"AUG","MonthlyBudget":328223.9600000000,"MonthlyPosted":250416.9300000000,"MonthlyVariance":77807.03000000000,"YTDBudget":2580588.330000000,"YTDPosted":2075383.600000000,"YTDVariance":505204.7300000000,"AnnualBudget":3879559.340000000,"AnnualPosted":1936905.600000000,"AnnualVariance":1942653.740000000},"SEP":{"Month":"SEP","MonthlyBudget":313274.8700000000,"MonthlyPosted":-76566.64000000000,"MonthlyVariance":389841.5100000000,"YTDBudget":2827196.560000000,"YTDPosted":1961361.240000000,"YTDVariance":865835.3200000000,"AnnualBudget":3779559.380000000,"AnnualPosted":1900049.880000000,"AnnualVariance":1879509.500000000},"OCT":{"Month":"OCT","MonthlyBudget":0,"MonthlyPosted":-61311.36000000000,"MonthlyVariance":61311.36000000000,"YTDBudget":1764331.340000000,"YTDPosted":1231178.370000000,"YTDVariance":533152.9700000000,"AnnualBudget":2362354.180000000,"AnnualPosted":1231178.370000000,"AnnualVariance":1131175.810000000}}}');
if (obj.Account.NOV != null) {
novMonth = obj.Account.NOV.Month;
novAnnualPosted = obj.Account.NOV.AnnualPosted;
novAnnualVariance = obj.Account.NOV.AnnualVariance;
} else {
novMonth = 'NOV';
novAnnualPosted = 0;
novAnnualVariance = 0;
}
if (obj.Account.DEC != null) {
decMonth = obj.Account.DEC.Month;
decAnnualPosted = obj.Account.DEC.AnnualPosted;
decAnnualVariance = obj.Account.DEC.AnnualVariance;
} else {
decMonth = 'DEC';
decAnnualPosted = 0;
decAnnualVariance = 0;
}
var comma = ",";
var month = novMonth.concat(decMonth);
var randomColorFactor = function() {
return Math.round(Math.random() * 255);
};
var randomColor = function(opacity) {
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
};
var ComboChart = new Chart(document.getElementById("ComboChart"), {
type: 'bar',
data: {
labels: [obj.Account.JAN.Month, obj.Account.FEB.Month, obj.Account.MAR.Month, obj.Account.APR.Month, obj.Account.MAY.Month, obj.Account.JUN.Month, obj.Account.JUL.Month, obj.Account.AUG.Month, obj.Account.SEP.Month, obj.Account.OCT.Month, month],
datasets: [{
label: "Annual Posted",
type: "bar",
borderColor: 'rgb(115, 115, 115)',
backgroundColor: 'rgb(115, 115, 115)',
data: [obj.Account.JAN.AnnualPosted,
obj.Account.FEB.AnnualPosted,
obj.Account.MAR.AnnualPosted,
obj.Account.APR.AnnualPosted,
obj.Account.MAY.AnnualPosted,
obj.Account.JUN.AnnualPosted,
obj.Account.JUL.AnnualPosted,
obj.Account.AUG.AnnualPosted,
obj.Account.SEP.AnnualPosted,
obj.Account.OCT.AnnualPosted,
novAnnualPosted,
decAnnualPosted
],
fill: false
},
{
label: "Annual Variance",
type: "bar",
borderColor: 'rgb(210, 11, 11)',
backgroundColor: 'rgb(210, 11, 11)',
data: [obj.Account.JAN.AnnualVariance,
obj.Account.FEB.AnnualVariance,
obj.Account.MAR.AnnualVariance,
obj.Account.APR.AnnualVariance,
obj.Account.MAY.AnnualVariance,
obj.Account.JUN.AnnualVariance,
obj.Account.JUL.AnnualVariance,
obj.Account.AUG.AnnualVariance,
obj.Account.SEP.AnnualVariance,
obj.Account.OCT.AnnualVariance,
novAnnualVariance,
decAnnualVariance
],
fill: false
}
]
},
options: {
title: {
display: true,
text: 'YTD Budget vs Posted',
position: 'right'
},
legend: {
display: true,
position: 'bottom'
},
maintainAspectRatio: false,
responsive: true,
responsiveAnimationDuration: 1500,
scales: {
xAxes: [{
gridLines: {
display: false
}
}],
yAxes: [{
gridLines: {
display: false
},
ticks: {
beginAtZero: true,
callback: function(value, index, values) {
if (parseInt(value) >= 1000) {
return '$' + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
} else {
return '$' + value;
}
}
}
}]
},
}
});
document.getElementById("ComboChart").onclick = function(evt) {
var activePoints = ComboChart.getElementsAtEvent(evt);
var firstPoint = activePoints[0];
var label = ComboChart.data.labels[firstPoint._index];
var value = ComboChart.data.datasets[firstPoint._datasetIndex].data[firstPoint._index];
if (firstPoint !== undefined)
alert(label + ": " + value);
};
</script>
</div>
</div>
</body>
<body>
Related
I have a line chart which shows multiple lines. X-axis represents date and Y-axis represents numeric reading. The lines represent the category PZ-1, PZ-2 & PZ-3. I have managed to remove the horizontal line that connect between the dots but now I want to connect the dots vertically that aligns based on the date on X-axis. I do not want to rotate the line or swap X-axis position with Y-axis and vice versa. Does anyone know how I can achieve the vertical line? Thank you
Below is my current code:
const data = {
datasets: [
{label: 'PZ-1',data:[{x:'2022-02-25', y:40.551},{x:'2022-03-01', y:35.889},{x:'2022-03-02', y:34.68},{x:'2022-03-03', y:33.182},{x:'2022-03-04', y:30.82},{x:'2022-03-05', y:29.864},{x:'2022-03-08', y:28.413},{x:'2022-03-10', y:28.413},{x:'2022-03-12', y:28.424},{x:'2022-03-15', y:25.578},{x:'2022-03-17', y:27.07},{x:'2022-03-19', y:27.42},{x:'2022-03-22', y:27.478},{x:'2022-03-24', y:22.817},{x:'2022-03-26', y:22.576},{x:'2022-03-29', y:22.326},{x:'2022-03-31', y:22.011},{x:'2022-04-02', y:21.672},{x:'2022-04-05', y:21.561},{x:'2022-04-07', y:21.307},{x:'2022-04-09', y:34.988},{x:'2022-04-12', y:28.89},{x:'2022-04-14', y:28.618},{x:'2022-04-17', y:28.862},{x:'2022-04-19', y:27.727},{x:'2022-04-21', y:27.493},{x:'2022-04-23', y:27.149},{x:'2022-04-26', y:25.862},{x:'2022-04-28', y:25.59},{x:'2022-04-30', y:25.37},{x:'2022-05-04', y:24.79},{x:'2022-05-06', y:24.927}],backgroundColor: '#778899',borderColor: '#778899',borderWidth: 1,showLine: false},{label: 'PZ-2',data:[{x:'2022-02-22', y:40.994},{x:'2022-03-01', y:55.537},{x:'2022-03-02', y:62.907},{x:'2022-03-03', y:59.462},{x:'2022-03-04', y:55.175},{x:'2022-03-05', y:53.294},{x:'2022-03-08', y:50.284},{x:'2022-03-10', y:49.89},{x:'2022-03-12', y:50.334},{x:'2022-03-15', y:47.137},{x:'2022-03-17', y:48.726},{x:'2022-03-19', y:48.294},{x:'2022-03-22', y:48.002},{x:'2022-03-24', y:40.156},{x:'2022-03-26', y:39.857},{x:'2022-03-29', y:39.678},{x:'2022-03-31', y:39.331},{x:'2022-04-02', y:36.719},{x:'2022-04-05', y:36.438},{x:'2022-04-07', y:36.258},{x:'2022-04-09', y:72.891},{x:'2022-04-12', y:59.97},{x:'2022-04-14', y:59.578},{x:'2022-04-17', y:59.781},{x:'2022-04-19', y:60.408},{x:'2022-04-21', y:60.309},{x:'2022-04-23', y:59.82},{x:'2022-04-26', y:61.679},{x:'2022-04-28', y:61.539},{x:'2022-04-30', y:61.187},{x:'2022-05-04', y:59.871},{x:'2022-05-06', y:59.63}],backgroundColor: '#DB7093',borderColor: '#DB7093',borderWidth: 1,showLine: false},{label: 'PZ-3',data:[{x:'2022-02-22', y:51.455},{x:'2022-03-01', y:44.882},{x:'2022-03-02', y:58.791},{x:'2022-03-03', y:55.118},{x:'2022-03-04', y:48.364},{x:'2022-03-05', y:47.498},{x:'2022-03-08', y:45.477},{x:'2022-03-10', y:44.859},{x:'2022-03-12', y:45.468},{x:'2022-03-15', y:39.599},{x:'2022-03-17', y:40.561},{x:'2022-03-19', y:39.993},{x:'2022-03-22', y:40.232},{x:'2022-03-24', y:33.061},{x:'2022-03-26', y:33.169},{x:'2022-03-29', y:32.99},{x:'2022-03-31', y:32.849},{x:'2022-04-02', y:31.811},{x:'2022-04-05', y:31.412},{x:'2022-04-07', y:31.223},{x:'2022-04-09', y:84.506},{x:'2022-04-12', y:74.415},{x:'2022-04-14', y:74.079},{x:'2022-04-17', y:73.876},{x:'2022-04-19', y:87.873},{x:'2022-04-21', y:87.748},{x:'2022-04-23', y:87.45},{x:'2022-04-26', y:76.555},{x:'2022-04-28', y:76.401},{x:'2022-04-30', y:76.649},{x:'2022-05-04', y:75.585},{x:'2022-05-06', y:75.748}],backgroundColor: '#8B008B',borderColor: '#8B008B',borderWidth: 1,showLine: false}
]
};
// config
const config = {
type: 'line',
data,
options: {
layout: {
padding: {
left: 5
}
},
indexAxis: 'x',
scales: {
y: {
beginAtZero: true
},
x:{
reverse: false,
type: 'time',
time: {
tooltipFormat: 'dd-MMM-yy',
displayFormats: {
day: 'dd-MMM-yy'
}
},
ticks: {
source: 'date',
autoSkip: false
}
}
}
}
};
// render init block
const myChart = new Chart(
document.getElementById('myChart'),
config
);
* {
margin: 0;
padding: 0;
font-family: sans-serif;
}
.chartCard {
overflow:auto;
background: rgba(255, 26, 104, 0.2);
display: flex;
align-items: center;
justify-content: center;
}
.chartBox {
padding: 20px;
border-radius: 20px;
border: solid 3px rgba(255, 26, 104, 1);
background: white;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Line Chart</title>
</head>
<body>
<div class="chartCard">
<div class="chartBox">
<canvas id="myChart" style="position: relative;height:1200px;width:1400px"></canvas>
</div>
</div>
<script src="https://rawgit.com/moment/moment/2.2.1/min/moment.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
</body>
</html>
You can use the Plugin Core API and define a beforeDraw hook that draws the lines directly on the canvas through the CanvasRenderingContext2D.
Please take a look at your amended code and see how it works.
const data = {
datasets: [
{label: 'PZ-1',data:[{x:'2022-02-25', y:40.551},{x:'2022-03-01', y:35.889},{x:'2022-03-02', y:34.68},{x:'2022-03-03', y:33.182},{x:'2022-03-04', y:30.82},{x:'2022-03-05', y:29.864},{x:'2022-03-08', y:28.413},{x:'2022-03-10', y:28.413},{x:'2022-03-12', y:28.424},{x:'2022-03-15', y:25.578},{x:'2022-03-17', y:27.07},{x:'2022-03-19', y:27.42},{x:'2022-03-22', y:27.478},{x:'2022-03-24', y:22.817},{x:'2022-03-26', y:22.576},{x:'2022-03-29', y:22.326},{x:'2022-03-31', y:22.011},{x:'2022-04-02', y:21.672},{x:'2022-04-05', y:21.561},{x:'2022-04-07', y:21.307},{x:'2022-04-09', y:34.988},{x:'2022-04-12', y:28.89},{x:'2022-04-14', y:28.618},{x:'2022-04-17', y:28.862},{x:'2022-04-19', y:27.727},{x:'2022-04-21', y:27.493},{x:'2022-04-23', y:27.149},{x:'2022-04-26', y:25.862},{x:'2022-04-28', y:25.59},{x:'2022-04-30', y:25.37},{x:'2022-05-04', y:24.79},{x:'2022-05-06', y:24.927}],backgroundColor: '#778899',borderColor: '#778899',borderWidth: 1,showLine: false},{label: 'PZ-2',data:[{x:'2022-02-22', y:40.994},{x:'2022-03-01', y:55.537},{x:'2022-03-02', y:62.907},{x:'2022-03-03', y:59.462},{x:'2022-03-04', y:55.175},{x:'2022-03-05', y:53.294},{x:'2022-03-08', y:50.284},{x:'2022-03-10', y:49.89},{x:'2022-03-12', y:50.334},{x:'2022-03-15', y:47.137},{x:'2022-03-17', y:48.726},{x:'2022-03-19', y:48.294},{x:'2022-03-22', y:48.002},{x:'2022-03-24', y:40.156},{x:'2022-03-26', y:39.857},{x:'2022-03-29', y:39.678},{x:'2022-03-31', y:39.331},{x:'2022-04-02', y:36.719},{x:'2022-04-05', y:36.438},{x:'2022-04-07', y:36.258},{x:'2022-04-09', y:72.891},{x:'2022-04-12', y:59.97},{x:'2022-04-14', y:59.578},{x:'2022-04-17', y:59.781},{x:'2022-04-19', y:60.408},{x:'2022-04-21', y:60.309},{x:'2022-04-23', y:59.82},{x:'2022-04-26', y:61.679},{x:'2022-04-28', y:61.539},{x:'2022-04-30', y:61.187},{x:'2022-05-04', y:59.871},{x:'2022-05-06', y:59.63}],backgroundColor: '#DB7093',borderColor: '#DB7093',borderWidth: 1,showLine: false},{label: 'PZ-3',data:[{x:'2022-02-22', y:51.455},{x:'2022-03-01', y:44.882},{x:'2022-03-02', y:58.791},{x:'2022-03-03', y:55.118},{x:'2022-03-04', y:48.364},{x:'2022-03-05', y:47.498},{x:'2022-03-08', y:45.477},{x:'2022-03-10', y:44.859},{x:'2022-03-12', y:45.468},{x:'2022-03-15', y:39.599},{x:'2022-03-17', y:40.561},{x:'2022-03-19', y:39.993},{x:'2022-03-22', y:40.232},{x:'2022-03-24', y:33.061},{x:'2022-03-26', y:33.169},{x:'2022-03-29', y:32.99},{x:'2022-03-31', y:32.849},{x:'2022-04-02', y:31.811},{x:'2022-04-05', y:31.412},{x:'2022-04-07', y:31.223},{x:'2022-04-09', y:84.506},{x:'2022-04-12', y:74.415},{x:'2022-04-14', y:74.079},{x:'2022-04-17', y:73.876},{x:'2022-04-19', y:87.873},{x:'2022-04-21', y:87.748},{x:'2022-04-23', y:87.45},{x:'2022-04-26', y:76.555},{x:'2022-04-28', y:76.401},{x:'2022-04-30', y:76.649},{x:'2022-05-04', y:75.585},{x:'2022-05-06', y:75.748}],backgroundColor: '#8B008B',borderColor: '#8B008B',borderWidth: 1,showLine: false}]};
const config = {
type: 'line',
plugins: [{
beforeDraw: chart => {
var ctx = chart.ctx;
ctx.save();
ctx.strokeStyle = '#aaaaaa';
var xAxis = chart.scales.x;
var yAxis = chart.scales.y;
xAxis.ticks.forEach((t, i) => {
const dateString = moment(t.value).format('YYYY-MM-DD');
const values = chart.data.datasets
.filter((ds, i) => !chart.getDatasetMeta(i).hidden)
.map(ds => ds.data.find(v => v.x == dateString))
.filter(v => v != undefined)
.map(o => o.y);
if (values.length > 1) {
var x = xAxis.getPixelForTick(i);
var yTop = yAxis.getPixelForValue(Math.max(...values));
var yBottom = yAxis.getPixelForValue(Math.min(...values));
ctx.beginPath();
ctx.moveTo(x, yBottom);
ctx.lineTo(x, yTop);
ctx.stroke();
}
});
ctx.restore();
}
}],
data,
options: {
layout: {
padding: {
left: 5
}
},
scales: {
y: {
beginAtZero: true
},
x: {
offset: true,
type: 'time',
time: {
tooltipFormat: 'dd-MMM-yy',
displayFormats: {
day: 'dd-MMM-yy'
}
},
grid: {
display: false
},
ticks: {
source: 'date',
autoSkip: false
}
}
}
}
};
new Chart('myChart', config );
<script src="https://rawgit.com/moment/moment/2.2.1/min/moment.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
<canvas id="myChart" height="140"></canvas>
I am making a chess game with HTML, CSS and JS but i cannot figure out how to get the index of a piece in the grid before and after being dragged(i also use jquery and jquery-ui)
This is an image of the "game":
https://i.stack.imgur.com/w3qmG.png
CODE HTML
<div class="chessboard ">
<div class="cell white">
<img class="piece" id="rook" draggable="true" src="assets/pieces/black/rook.png" alt="">
</div>
<div class="cell black">
<img class="piece" id="knight" draggable="true" src="assets/pieces/black/knight.png" alt="">
</div>
..............................................
<div class="cell black">
<img class="piece" id="knight" draggable="true" src="assets/pieces/white/knight.png" alt="">
</div>
<div class="cell white">
<img class="piece" id="rook" draggable="true" src="assets/pieces/white/rook.png" alt="">
</div>
</div>
CODE JS
const pieces = document.querySelectorAll(".piece");
const grid = document.querySelector(".chessboard");
var grid_layout = []
// i tried to find at least the starting position by clicking on the pieces
pieces.forEach((piece) => {
piece.addEventListener("click", () => {
console.log($(piece.id).index())
})
})
This is one of my failed attempts
function GetElementIndex() {
if ($('.piece').hasClass("ui-draggable-dragging")) {
console.log(GetGridElementsPosition($('.piece').index()));
}
}
This function returns the position of an element, given the index(which i am looking for), fortunately this one works quite well
function GetGridElementsPosition(index) {
const colCount = $(".chessboard")
.css("grid-template-columns")
.split(" ").length;
const rowPosition = Math.floor(index / colCount) + 1;
const colPosition = (index % colCount) + 1;
return { row: rowPosition, column: colPosition };
}
Below is the function that enables the pieces to be dragged
$(function () {
$(".piece").draggable({
start: function () {
console.log("Starting Index:");
GetElementIndex();
},
stop: function () {
console.log("Ending Index:");
GetElementIndex();
},
grid: [100, 100],
activeClass: "dragging",
});
});
EDIT
So due to some help from the comments i managed to get what i needed. I gave every cell of the grid an x and y value (like {1, 1} or {5, 8}) and i rendered the pieces through js and not html. I then found their current position by doing this:
var col = Math.ceil($(this).position().left / 100);
var row = Math.ceil($(this).position().top / 100);
This is just the piece being dragged(inside function in draggable())
The easiest way maybe to add at the html generation a position to the cells with data-x and data-yas custom html attributes? https://www.w3schools.com/tags/att_global_data.asp
It's very dependent on how did you implemented chessboard.
Btw you made me try to maky one myself :)
It' just board and moving pieces .. they cant attack or move properly (by the rules).
Chessboard on codepen.io
const game = document.getElementById("game");
const squares = [];
const createSquare = (color, index) => {
const el = document.createElement("div");
el.classList.add(color, "square");
el.dataset.index = index;
el.dataset.color = color;
game.appendChild(el);
squares.push(el);
document.addEventListener("click", movePiece);
};
let c = 0;
const createChessboard = () => {
for (let i = 0; i < 8; i++) {
for (let j = 0; j < 8; j++) {
let color;
if (i % 2) {
color = j % 2 ? "white" : "black";
} else {
color = j % 2 ? "black" : "white";
}
createSquare(color, c);
c++;
}
}
};
createChessboard();
const playerWhite = {
side: "white",
pieces: {
rook1: { name: "Rook", side: "white", index: 0 },
knight1: { name: "Knight", side: "white", index: 1 },
bishop1: { name: "Bishop", side: "black", index: 2 },
king: { name: "King", side: "white", index: 3 },
queen: { name: "Queen", side: "white", index: 4 },
bishop2: { name: "Bishop", side: "black", index: 5 },
knight2: { name: "Knight", side: "white", index: 6 },
rook2: { name: "Rook", side: "white", index: 7 },
pawn1: { name: "Pawn", side: "white", index: 8 },
pawn2: { name: "Pawn", side: "white", index: 9 },
pawn3: { name: "Pawn", side: "white", index: 10 },
pawn4: { name: "Pawn", side: "white", index: 11 },
pawn5: { name: "Pawn", side: "white", index: 12 },
pawn6: { name: "Pawn", side: "white", index: 13 },
pawn7: { name: "Pawn", side: "white", index: 14 },
pawn8: { name: "Pawn", side: "white", index: 15 }
}
};
const playerBlack = {
side: "black",
pieces: {
rook1: { name: "Rook", side: "black", index: 56 },
knight1: { name: "Knight", side: "black", index: 57 },
bishop1: { name: "Bishop", side: "black", index: 58 },
king: { name: "King", side: "black", index: 60 },
queen: { name: "Queen", side: "black", index: 59 },
bishop2: { name: "Bishop", side: "black", index: 61 },
knight2: { name: "Knight", side: "white", index: 62 },
rook2: { name: "Rook", side: "black", index: 63 },
pawn1: { name: "Pawn", side: "black", index: 48 },
pawn2: { name: "Pawn", side: "black", index: 49 },
pawn3: { name: "Pawn", side: "black", index: 50 },
pawn4: { name: "Pawn", side: "black", index: 51 },
pawn5: { name: "Pawn", side: "black", index: 52 },
pawn6: { name: "Pawn", side: "black", index: 53 },
pawn7: { name: "Pawn", side: "black", index: 54 },
pawn8: { name: "Pawn", side: "black", index: 55 }
}
};
const setPlayerFigures = (player) => {
for (let piece in player.pieces) {
const thePiece = player.pieces[piece];
const el = document.createElement("div");
el.classList.add(player.side, "piece");
el.dataset.id = `${player.side}~${piece}`;
const text = document.createElement("span");
text.textContent = thePiece.name;
el.appendChild(text);
squares[thePiece.index].appendChild(el);
thePiece.square = squares[thePiece.index];
}
};
setPlayerFigures(playerWhite);
setPlayerFigures(playerBlack);
let selectedSquare = null;
function movePiece(e) {
const selectedPiece = selectedSquare && selectedSquare.childNodes[0];
const clickedPiece = e.target.childNodes[0];
const clickedSquare = e.target;
// console.log(selectedPiece)
// console.log(clickedPiece);
if (selectedPiece === clickedPiece) {
clickedPiece.classList.remove("active");
selectedSquare = null;
return;
}
if (selectedPiece && clickedPiece) {
// trying to move to another piece
// there will be code for 'take' the piece
//FOR NOW just select another piece
selectedPiece.classList.remove("active");
clickedPiece.classList.add("active");
selectedSquare = clickedSquare;
// TODO --> delete this
return;
}
if (selectedSquare && !clickedPiece && selectedPiece) {
selectedPiece.classList.remove("active");
clickedSquare.appendChild(selectedPiece);
selectedSquare = null;
return;
}
selectedSquare = clickedSquare;
clickedPiece && clickedPiece.classList.add("active");
}
#game {
border: 2px double gray;
display: flex;
flex-wrap: wrap;
width: 400px;
}
.square {
width: 46px;
height: 46px;
display: flex;
justify-content: center;
align-items: center;
transition: border 0.1s;
cursor: pointer;
}
.square.white {
background: #ccc;
border: 2px solid #ccc;
}
.square.white:hover {
border: 2px solid #888;
}
.square.black {
background: #444;
border: 2px solid #444;
}
.square.black:hover {
border: 2px solid #888;
}
.piece {
pointer-events: none;
border-radius: 50%;
width: 35px;
height: 35px;
display: flex;
justify-content: center;
align-items: center;
font-size: 0.7rem;
}
.piece.white {
background: #fff;
border: 2px solid black;
color: black;
}
.piece.black {
background: #000;
border: 2px solid white;
color: white;
}
.piece.active {
border-color: #20e320;
}
<div id="game"></div>
Hello guys I would like to know how to add percentage like image below.
I want to add text which contain the percentage of each data.
It's gonna be between pie-chart and legend.
How to add the text or maybe change the style of legend by adding text on the above of legends.
What I've done so far
HTML
<div class="block__chart">
<div id="counter"></div>
<canvas id="myChart"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
CSS
* {
padding: 0;
margin: 0;
font-family: "Montserrat", sans-serif;
}
.block__chart {
width: 100%;
background-color: #eee;
padding: 2em;
display: flex;
flex-direction: column;
}
#myChart {
width: 100%;
margin: 1em 0;
}
canvas {
width: 452px;
}
#counter {
text-align: center;
font-size: 2em;
font-weight: 700;
color: #007cbd;
p {
color: black;
font-weight: 400;
}
}
.activeUser {
font-size: 20px;
}
.lastFiveMins {
font-size: 10px;
}
JS
// Fake Data - Total 46946
var dataset = [
{ label: "Mobile", count: 18778, color: "#72bbe1" },
{ label: "Desktop", count: 28168, color: "#3ea6dd" }
];
var dataTotal = dataset.reduce((acc, data) => (acc += data.count), 0);
var counter = document.getElementById("counter");
counter.innerHTML = `
${dataTotal}
<p class="activeUser">ACTIVE USER</p>
<p class="lastFiveMins">IN THE LAST 5 MINUTES</p>`;
var ctx = document.getElementById("myChart").getContext("2d");
var myChart = new Chart(ctx, {
type: "pie",
data: {
labels: [dataset[0].label.toUpperCase(), dataset[1].label.toUpperCase()],
datasets: [
{
data: [dataset[0].count, dataset[1].count],
backgroundColor: [dataset[0].color, dataset[1].color],
borderWidth: 1
}
]
},
options: {
tooltips: {
callbacks: {
label: function (tooltipItem, data) {
let allData = data.datasets[tooltipItem.datasetIndex].data;
let sumData = allData.reduce((memo, data) => (memo += data), 0);
let tooltipLabel = data.labels[tooltipItem.index];
let tooltipData = allData[tooltipItem.index];
let tooltipPercentageCalc = (tooltipData / sumData) * 100;
let tooltipPercentage = `${tooltipPercentageCalc.toFixed(1)}%`;
return `${tooltipLabel} : ${tooltipData} (${tooltipPercentage})`;
}
}
},
legend: {
display: true,
labels: {
usePointStyle: true,
text: "Hello World",
fontColor: "black",
fontSize: 15
},
position: "bottom"
}
}
});
you can use filter options for customizing your labels
example down below
legend: {
display: true,
labels: {
usePointStyle: true,
text: "Hello World",
fontColor: "black",
fontSize: 15,
filter: function(legendItem, data) {
let labels = data.labels, datasets = data.datasets[0].data;
for(let i=0;i<labels.length;i++){
if(labels[i].indexOf(legendItem.text)!=-1){
let t = legendItem.text;
legendItem.text = t+' : '+datasets[i] + '%';
break;
}
}
return legendItem;
},
},
position: "bottom"
}
i have a question - how to draw canvas layer (for example just simple square) with event on click on button in Vue.js? I have stage and on that stage with position x:0, y:0 i want after click on button to generate that square and with drag and drop to position it on that stage? I'm using Konvajs for creating Canvas
Can somebody help me?
<template>
<div id="main">
<h1></h1>
<div id="obszarroboczy" style="width: 500px; height: 600px;">
<v-stage ref="stage"
:config="configKonva"
#dragstart="handleDragstart"
#dragend="handleDragend">
<v-layer ref="layer">
<v-star
v-for="item in list"
:key="item.id"
:config="item"></v-star>
</v-layer>
<v-layer ref="dragLayer"></v-layer>
</v-stage>
</div>
<div class="col-md-6">
<button v-on:click="handleClick" id="more_canvas">More</button>
</div>
</div>
</template>
<script>
import Vue from "vue";
import axios from "axios";
import draggable from "vuedraggable";
import swal from "sweetalert2";
import VueKonva from "vue-konva";
export default {
name: "EnumCurrencyIndex",
$mount: "#main",
components: {
draggable
},
data() {
return {
model: [],
editable: true,
isDragging: false,
delayedDragging: false,
type: "currency",
editedElement: null,
newElement: "",
list: [],
configKonva: {
width: 400,
height: 400
},
configCircle: {
x: 100,
y: 100,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4
},
vm: {}
};
},
beforeMount() {
this.fetchData();
},
computed: {
dragOptions() {
return {
animation: 0,
group: "description",
disabled: !this.editable,
ghostClass: "ghost"
};
},
listString() {
return this.model;
},
dragCanvas() {
return this.model;
}
},
watch: {
$route: "fetchData",
isDragging(newValue) {
if (newValue) {
this.delayedDragging = true;
return;
}
this.$nextTick(() => {
this.delayedDragging = false;
});
}
},
methods: {
handleDragstart(starComponent) {
var vm = this;
const shape = starComponent.getStage();
const dragLayer = vm.$refs.dragLayer.getStage();
const stage = vm.$refs.stage.getStage();
// moving to another layer will improve dragging performance
shape.moveTo(dragLayer);
stage.draw();
starComponent.config.shadowOffsetX = 15;
starComponent.config.shadowOffsetY = 15;
starComponent.config.scaleX = starComponent.config.startScale * 1.2;
starComponent.config.scaleY = starComponent.config.startScale * 1.2;
},
handleDragend(starComponent) {
var vm = this;
const shape = starComponent.getStage();
const layer = vm.$refs.layer.getStage();
const stage = vm.$refs.stage.getStage();
shape.moveTo(layer);
stage.draw();
shape.to({
duration: 0.5,
easing: Konva.Easings.ElasticEaseOut,
scaleX: starComponent.config.startScale,
scaleY: starComponent.config.startScale,
shadowOffsetX: 5,
shadowOffsetY: 5
});
},
handleClick(configCircle) {
var vm = this;
const shape = vm.$refs.layer.getStage();
const layer = vm.$refs.layer.getStage();
const stage = vm.$refs.stage.getStage();
console.log(1);
layer.add(configCircle);
stage.add(layer);
},
haveIntersection(r1, r2) {
return !(
r2.x > r1.x + r1.width ||
r2.x + r2.width < r1.x ||
r2.y > r1.y + r1.height ||
r2.y + r2.height < r1.y
);
},
orderList() {
this.model = this.model.sort((one, two) => {
return one.position - two.position;
});
},
onMove({ relatedContext, draggedContext }) {
const relatedElement = relatedContext.element;
const draggedElement = draggedContext.element;
return (
(!relatedElement || !relatedElement.fixed) && !draggedElement.fixed
);
},
fetchData() {
var vm = this;
axios
.get(`/api/${this.resource}?type=${this.type}`)
.then(function(response) {
Vue.set(vm.$data, "model", response.data.model);
})
.catch(function(error) {
console.log(error);
});
}
},
mounted() {
var box = document.getElementById("obszarroboczy");
this.configKonva.width = box.offsetWidth;
this.configKonva.height = box.offsetHeight;
var vm = this;
for (let n = 0; n < 30; n++) {
const scale = Math.random();
const stage = vm.$refs.stage.getStage();
vm.list.push({
x: Math.random() * stage.getWidth(),
y: Math.random() * stage.getHeight(),
rotation: Math.random() * 180,
numPoints: 5,
innerRadius: 30,
outerRadius: 50,
fill: "#89b717",
opacity: 0.8,
draggable: true,
scaleX: scale,
scaleY: scale,
shadowColor: "black",
shadowBlur: 10,
shadowOffsetX: 5,
shadowOffsetY: 5,
shadowOpacity: 0.6,
startScale: scale
});
};
},
directives: {
"element-focus": function(el, binding) {
if (binding.value) {
el.focus();
}
}
}
};
</script>
<style>
#obszarroboczy {
width: 100px;
height: 300px;
}
.normal {
background-color: grey;
}
.table td {
width: 100px;
height: 100px;
background: white;
border: 2px dotted black;
max-width: 100px;
padding: 5px;
}
.drag {
display: flex;
flex-direction: row;
}
.list {
flex-grow: 1;
max-width: 47%;
margin-right: 40px;
}
.name {
width: 50%;
display: inline-block;
height: 50px;
background: pink;
border: 5px green solid;
box-sizing: border-box;
padding: 5px;
}
.name.large {
width: 100%;
}
.dragArea {
min-height: 100px;
}
.dragArea img {
margin: 3px;
cursor: pointer;
}
</style>
var mainCanvas = new Vue({
el: '#main', // the element where the method wil lrender the canvas to
data: {
name: 'Vue.js'
},
methods: {
handleClick: function (event) { // handleClick is the method name for the button
var stage = new Konva.Stage({ // this line till the stage.add() line renders the draggable square
container: 'obszarroboczy',
width: 500,
height: 500
});
var layer = new Konva.Layer();
var rect = new Konva.Rect({
x: 0,
y: 0,
width: 100,
height: 100,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
layer.add(rect);
stage.add(layer);
}
}
});
I added comments to explain what certain important lines does but you can check out the official KonvaJS Docs in GitHub for a more detailed explanation on what each line above does.
I've been trying to format the tooltips of my charts into percentages with no success. Here are the following features of my project reduced to bare minimum:
UI consists of 2 groups of radio buttons and 1 checkmark.
User can switch to view data sources displayed by different chart types.
Source is queried from 7 Google Sheets that the user can view.
The user can view these data sources with 4 chart types.
The initial data source is displayed by a chartWrapper() (chart called by drawChart())
The next 6 data sources share one chartWrapper() (main called by alterChart())
I've have commented within the M.C.V.E. details of the 3 failed attempts, they are as follows:
First attempt: Adding encoded query string to data source url.
Second attempt: Using setQuery()
Third attempt: Using formatter object.
Any help to resolve this by using any of the 3 ways previously mentioned is welcome. I'm open to anything I didn't cover, but I may need more details since I'm still learning this API. Thank you for your valuable time, I appreciate it.
[PLUNKER]
SNIPPET
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1, user-scalable=no">
<title>MCVE - GVis Formatting Tooltips</title>
<link href='https://glpro.s3.amazonaws.com/_css/glb.css' rel='stylesheet'>
<style>
#import url('https://fonts.googleapis.com/css?family=Open+Sans');
*,
*:before,
*:after {
font-style: normal !important;
}
body {
position: relative;
}
form {
background-color: #333;
}
#ii {
margin-top: 80px
}
.panel {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
#chart {
height: 70vh;
width: 96vw;
}
.group.group:after,
.chart.chart:after,
.root.root:after {
color: #333;
}
div.google-visualization-tooltip {
background-color: rgba(0, 0, 0, .6);
border-radius: 6px;
min-width: 325px;
max-height: 75px;
}
div.google-visualization-tooltip > ul > li {
display: table-cell;
margin: 0 5px;
}
div.google-visualization-tooltip > ul > li > span {
color: gold;
}
#groupOpt {
display: none;
}
#groupOpt.on {
display: block;
}
</style>
</head>
<body class='sl'>
<header class='panel'>
<!--THIS PART REMOVED-->
</form>
</header>
<section id="ii">
<h1>Sources</h1>
<figure id='chart'></figure>
</section>
<footer>
</footer>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load('current', {
packages: ['corechart']
});
google.charts.setOnLoadCallback(drawChart);
var options = {
backgroundColor: {
fill: 'transparent'
},
tooltip: {
textStyle: {
color: 'gold',
fontSize: 16,
fontName: 'Verdana'
},
trigger: 'focus',
isHtml: true
},
animation: {
startup: true,
duration: 1000,
easing: 'out'
},
title: 'Percentage of Americans in Favor of Same-sex Marriage (2001-16)',
titleTextStyle: {
color: 'gold',
fontName: 'Open Sans',
fontSize: 22
},
hAxis: {
textStyle: {
color: 'cyan'
},
title: 'Year',
titleTextStyle: {
color: 'gold',
fontName: 'Open Sans',
fontSize: 22
},
format: '####'
},
vAxis: {
maxValue: .85,
format: '#%',
textStyle: {
fontName: 'Open Sans',
color: 'cyan'
},
title: 'Percentage of Sub-Population that Approves of Same-sex Marriage',
titleTextStyle: {
color: 'gold',
fontName: 'Arial',
fontSize: 16
}
},
legend: {
textStyle: {
color: 'white',
fontName: 'Verdana'
},
position: 'bottom'
},
crosshair: {
trigger: 'both',
orientation: 'both',
focused: {
color: 'gold',
opacity: .7
},
selected: {
color: 'cyan',
opacity: .7
}
},
pointSize: 12,
theme: 'materials',
chartArea: {
left: 100,
top: 75,
width: '90%',
height: '60%'
}
}
var dataTable;
var chart;
var data;
var main;
var cArray = ['LineChart', 'AreaChart', 'ColumnChart', 'ScatterChart'];
var qArray = [THIS DATA REMOVED]
];
/* Attempt #1 - Using encoded query string with data query source url
// No Errors - QUERY_STRING = select * (# * 100)% -- Syntax is wrong, but I couldn't find any solid examples.
var qArray = [ 'https://docs.google.com/spreadsheets/d/1EY13wZB9IUet4e5gVeMEFLQcHdNfr--S4j741XVAfxo/gviz/tq?gid=1104711743&select%20*%20(%23%20*%20100)%25','https://docs.google.com/spreadsheets/d/1EH9C-_OviFTLwC5Z30HTATZqtFnOx_JcOIDHYzF7-FY/gviz/tq?gid=1552974580&select%20*%20(%23%20*%20100)%25 ',
'https://docs.google.com/spreadsheets/d/1EY13wZB9IUet4e5gVeMEFLQcHdNfr--S4j741XVAfxo/gviz/tq?gid=2111420909&select%20*%20(%23%20*%20100)%25', 'https://docs.google.com/spreadsheets/d/1EY13wZB9IUet4e5gVeMEFLQcHdNfr--S4j741XVAfxo/gviz/tq?gid=1379142381&select%20*%20(%23%20*%20100)%25', 'https://docs.google.com/spreadsheets/d/1EY13wZB9IUet4e5gVeMEFLQcHdNfr--S4j741XVAfxo/gviz/tq?gid=1749299608&select%20*%20(%23%20*%20100)%25', 'https://docs.google.com/spreadsheets/d/1EY13wZB9IUet4e5gVeMEFLQcHdNfr--S4j741XVAfxo/gviz/tq?gid=268750266&select%20*%20(%23%20*%20100)%25', 'https://docs.google.com/spreadsheets/d/1EY13wZB9IUet4e5gVeMEFLQcHdNfr--S4j741XVAfxo/gviz/tq?gid=148086622&select%20*%20(%23%20*%20100)%25', 'https://docs.google.com/spreadsheets/d/1EY13wZB9IUet4e5gVeMEFLQcHdNfr--S4j741XVAfxo/gviz/tq?gid=1474413858&select%20*%20(%23%20*%20100)%25'
];
*/ //
function drawChart() {
chart = new google.visualization.ChartWrapper();
chart.setDataSourceUrl(qArray[1]);
/* Attempt #2 - Using setQuery
// Error -- Syntax is wrong but as stated previously, I could not find a solid example.
INVALID_QUERY
Invalid query: PARSE_ERROR: Encountered "format" at line 1, column 8. Was expecting one of: "true" ... "false" ... "date" ... "timeofday" ... "datetime" ... "timestamp" ... "min" ... "max" ... "avg" ... "count" ... "sum" ... "no_values" ... "no_format" ... "is" ... "null" ... "year" ... "month" ... "day" ... "hour" ... "minute" ... "second" ... "millisecond" ... "with" ... "contains" ... "starts" ... "ends" ... "matches" ... "like" ... "now" ... "dateDiff" ... "quarter" ... "lower" ... "upper" ... "dayOfWeek" ... "toDate" ... <ID> ... <INTEGER_LITERAL> ... <DECIMAL_LITERAL> ... <STRING_LITERAL> ... <QUOTED_ID> ... "(" ... "*" ... "-" ...
*/
//chart.setQuery('select format #%');
dataTable = new google.visualization.DataTable();
/* Attempt #3 Using formatter object
// Uncaught Error: Table has no columns -- I believe the syntax is correct, just not it's location and/or specific requirements such as properly handling the data from the ResponseQuery?
formatter = new google.visualization.NumberFormat({pattern:'#%'});
formatter.format(dataTable, 1);
formatter.format(dataTable, 2);
formatter.format(dataTable, 3);
formatter.format(dataTable, 4);
formatter.format(dataTable, 5);
formatter.format(dataTable, 6);
formatter.format(dataTable, 7);
formatter.format(dataTable, 8);
formatter.format(dataTable, 9);
formatter.format(dataTable, 10);
formatter.format(dataTable, 11);
formatter.format(dataTable, 12);
formatter.format(dataTable, 13);
formatter.format(dataTable, 14);
formatter.format(dataTable, 15);
*/
chart.setChartType('LineChart');
chart.setContainerId('chart');
chart.setOptions(options);
chart.draw();
}
function alterChart(C, Q) {
C = Number(C);
Q = Number(Q);
var URL = qArray[Q];
var VIS = cArray[C];
main = new google.visualization.ChartWrapper();
main.setDataSourceUrl(URL);
// Attempt #2
// main.setQuery('select format #%');
data = new google.visualization.DataTable();
/* Attempt #3
// Uncaught Error: Table has no columns
pattern = new google.visualization.NumberFormat({pattern:'#%'});
pattern.format(data, 1);
pattern.format(data, 2);
pattern.format(data, 3);
pattern.format(data, 4);
pattern.format(data, 5);
pattern.format(data, 6);
pattern.format(data, 7);
pattern.format(data, 8);
pattern.format(data, 9);
pattern.format(data, 10);
pattern.format(data, 11);
pattern.format(data, 12);
pattern.format(data, 13);
pattern.format(data, 14);
pattern.format(data, 15);
*/ //
main.setChartType(VIS);
main.setContainerId('chart');
main.setOptions(options);
main.draw();
}
$('#chartOpt, #groupOpt, #rootOpt').on('change', function(e) {
var chartSel = $("input[name='chart']:checked").val();
var groupSel = $("input[name='group']:checked").val();
if (e.target !== e.currentTarget) {
var target = e.target.id;
var status = $(target).hasClass('on') ? true : false;
}
if (target === 'root0') {
$('#' + target).toggleClass('on');
if (status === true) {
$('#groupOpt').slideUp().removeClass('on');
return alterChart(chartSel, '1');
} else if (status === false) {
$('#groupOpt').slideDown().addClass('on');
return alterChart(chartSel, groupSel);
} else return false;
} else if (target === 'chart0' || target === 'chart1' || target === 'chart2' || target === 'chart3') {
if (status === true) {
return alterChart(chartSel, '1');
} else {
return alterChart(chartSel, groupSel);
}
} else {
if (status === true) {
return false;
} else {
return alterChart(chartSel, groupSel);
}
}
});
var group = document.getElementsByName('group');
var len = group.length;
var rad;
var i;
for (i = 0; i < len; i++) {
group[i].onclick = function() {
if (rad == this) {
this.checked = false;
rad = null;
} else {
rad = this;
}
}
}
</script>
<!--<script src='gvis-api.js'></script>-->
</body>
</html>
You were setting the chart's data source URL and it was rendering itself before you had a chance to format the data. The solution is to query the data yourself, format it, and then pass it into the chart.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1, user-scalable=no">
<title>MCVE - GVis Formatting Tooltips</title>
<link href='https://glpro.s3.amazonaws.com/_css/glb.css' rel='stylesheet'>
<style>
#import url('https://fonts.googleapis.com/css?family=Open+Sans');
*, *:before, *:after { font-style: normal !important; }
body { position: relative; }
form { background-color: #333; }
#ii { margin-top: 80px }
.panel { display: flex; flex-wrap:wrap; justify-content: center; align-items: center; }
#chart { height: 70vh; width: 96vw; }
.group.group:after, .chart.chart:after, .root.root:after { color: #333; }
div.google-visualization-tooltip { background-color: rgba(0,0,0,.6); border-radius: 6px; min-width: 325px; max-height: 75px;}
div.google-visualization-tooltip > ul > li { display: table-cell; margin:0 5px; }
div.google-visualization-tooltip > ul > li > span { color: gold; }
#groupOpt { display:none; }
#groupOpt.on { display:block;}
</style>
</head>
<body class='sl'>
<header class='panel'>
<form id="rootOpt" class="sgc" style="width: 20%; color: #fffff">
<input type="checkbox" name="group" id="root0" value='1' checked>
<label for="root0" class="root" id="switch0" data-value="Results">Groups</label>
</form>
<form id="chartOpt" class="sgc" style="width: 80%; color: #ffcc00">
<input type="radio" name="chart" id="chart0" value='0' checked>
<input type="radio" name="chart" id="chart1" value='1'>
<input type="radio" name="chart" id="chart2" value='2'>
<input type="radio" name="chart" id="chart3" value='3'>
<label for="chart0" class="chart" data-value="Line Chart">Line Chart</label>
<label for="chart1" class="chart" data-value="Area Chart">Area Chart</label>
<label for="chart2" class="chart" data-value="Column Chart">Column Chart</label>
<label for="chart3" class="chart" data-value="Scatter Chart">Scatter Chart</label>
</form>
<form id="groupOpt" class='sgc' style="width:100%; color: #00ffff; display:none">
<input type="radio" name="group" id="group0" data-format='4' value='2' checked>
<input type="radio" name="group" id="group1" data-format='5' value='3'>
<input type="radio" name="group" id="group2" data-format='3' value='4'>
<input type="radio" name="group" id="group3" data-format='3' value='5'>
<input type="radio" name="group" id="group4" data-format='2' value='6'>
<input type="radio" name="group" id="group5" data-format='2' value='7'>
<label for="group0" class="group" data-value="Generation">Generation</label>
<label for="group1" class="group" data-value="Religion">Religion</label>
<label for="group2" class="group" data-value="Party Affiliation">Party Affiliation</label>
<label for="group3" class="group" data-value="Political Ideology">Political Ideology</label>
<label for="group4" class="group" data-value="Race">Race</label>
<label for="group5" class="group" data-value="Gender">Gender</label>
</form>
</header>
<section id="ii">
<h1>Sources</h1>
<figure id='chart'></figure>
</section>
<footer>
</footer>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
google.setOnLoadCallback(runQuery);
google.load('visualization', '1', {
packages: ['corechart', 'table', 'geomap']
});
var options = {
backgroundColor: {
fill: 'transparent'
},
tooltip: {
textStyle: {
color: 'gold',
fontSize: 16,
fontName: 'Verdana'
},
trigger: 'focus',
isHtml: true
},
animation: {
startup: true,
duration: 1000,
easing: 'out'
},
title: 'Percentage of Americans in Favor of Same-sex Marriage (2001-16)',
titleTextStyle: {
color: 'gold',
fontName: 'Open Sans',
fontSize: 22
},
hAxis: {
textStyle: {
color: 'cyan'
},
title: 'Year',
titleTextStyle: {
color: 'gold',
fontName: 'Open Sans',
fontSize: 22
},
format: '####'
},
vAxis: {
maxValue: .85,
format: '#%',
textStyle: {
fontName: 'Open Sans',
color: 'cyan'
},
title: 'Percentage of Sub-Population that Approves of Same-sex Marriage',
titleTextStyle: {
color: 'gold',
fontName: 'Arial',
fontSize: 16
}
},
legend: {
textStyle: {
color: 'white',
fontName: 'Verdana'
},
position: 'bottom'
},
crosshair: {
trigger: 'both',
orientation: 'both',
focused: {
color: 'gold',
opacity: .7
},
selected: {
color: 'cyan',
opacity: .7
}
},
pointSize: 12,
theme: 'materials',
chartArea: {
left: 100,
top: 75,
width: '90%',
height: '60%'
}
}
var dataTable;
var chart;
var data;
var main;
var cArray = ['LineChart', 'AreaChart', 'ColumnChart', 'ScatterChart'];
var qArray = ['https://docs.google.com/spreadsheets/d/1EY13wZB9IUet4e5gVeMEFLQcHdNfr--S4j741XVAfxo/gviz/tq?gid=1104711743', 'https://docs.google.com/spreadsheets/d/1EH9C-_OviFTLwC5Z30HTATZqtFnOx_JcOIDHYzF7-FY/gviz/tq?gid=1552974580',
'https://docs.google.com/spreadsheets/d/1EY13wZB9IUet4e5gVeMEFLQcHdNfr--S4j741XVAfxo/gviz/tq?gid=2111420909', 'https://docs.google.com/spreadsheets/d/1EY13wZB9IUet4e5gVeMEFLQcHdNfr--S4j741XVAfxo/gviz/tq?gid=1379142381', 'https://docs.google.com/spreadsheets/d/1EY13wZB9IUet4e5gVeMEFLQcHdNfr--S4j741XVAfxo/gviz/tq?gid=1749299608', 'https://docs.google.com/spreadsheets/d/1EY13wZB9IUet4e5gVeMEFLQcHdNfr--S4j741XVAfxo/gviz/tq?gid=268750266', 'https://docs.google.com/spreadsheets/d/1EY13wZB9IUet4e5gVeMEFLQcHdNfr--S4j741XVAfxo/gviz/tq?gid=148086622', 'https://docs.google.com/spreadsheets/d/1EY13wZB9IUet4e5gVeMEFLQcHdNfr--S4j741XVAfxo/gviz/tq?gid=1474413858'
];
function runQuery() {
var opts = {
sendMethod: 'auto'
};
if (!google.visualization) return;
var query = new google.visualization.Query(qArray[1], opts);
query.send(handleQueryResponse);
}
function handleQueryResponse(response) {
if (!response) return;
if (!response.getDataTable()) return;
drawChart(response.getDataTable());
}
function drawChart(dataTable) {
console.debug(dataTable)
chart = new google.visualization.LineChart(document.getElementById('chart'));
if (dataTable && dataTable.getNumberOfColumns() > 0) {
var formatter = new google.visualization.NumberFormat({
pattern: '#%',
fractionDigits: 0
});
formatter.format(dataTable, 1);
formatter.format(dataTable, 2);
}
chart.draw(dataTable, options);
}
function alterChart(C, Q) {
C = Number(C);
Q = Number(Q);
var URL = qArray[Q];
var VIS = cArray[C];
main = new google.visualization.ChartWrapper();
main.setDataSourceUrl(URL);
if (dataTable.getNumberOfColumns() > 0) {
//formatter = new google.visualization.NumberFormat({pattern:'#\'%\''});
var formatter = new google.visualization.NumberFormat({
pattern: '#%',
fractionDigits: 0
});
formatter.format(dataTable, 1);
formatter.format(dataTable, 2);
}
main.setChartType(VIS);
main.setContainerId('chart');
main.setOptions(options);
main.draw();
}
runQuery();
$('#chartOpt, #groupOpt, #rootOpt').on('change', function(e) {
var chartSel = $("input[name='chart']:checked").val();
var groupSel = $("input[name='group']:checked").val();
if (e.target !== e.currentTarget) {
var target = e.target.id;
var status = $(target).hasClass('on') ? true : false;
}
if (target === 'root0') {
$('#' + target).toggleClass('on');
if (status === true) {
$('#groupOpt').slideUp().removeClass('on');
return alterChart(chartSel, '1');
} else if (status === false) {
$('#groupOpt').slideDown().addClass('on');
return alterChart(chartSel, groupSel);
} else return false;
} else if (target === 'chart0' || target === 'chart1' || target === 'chart2' || target === 'chart3') {
if (status === true) {
return alterChart(chartSel, '1');
} else {
return alterChart(chartSel, groupSel);
}
} else {
if (status === true) {
return false;
} else {
return alterChart(chartSel, groupSel);
}
}
});
var group = document.getElementsByName('group');
var len = group.length;
var rad;
var i;
for (i = 0; i < len; i++) {
group[i].onclick = function() {
if (rad == this) {
this.checked = false;
rad = null;
} else {
rad = this;
}
}
}
</script>
</body>
</html>
I just resolved my issue, I formatted the actual source in Google Sheets and now the tooltips are percentages. So simple it never occurred to me. The following array is the only thing changed. Each new url points to a preformatted table in Google Sheets:
var qArray = ['https://docs.google.com/spreadsheets/d/1c9xyhR1_BavywsAOvpkf65Nhf9o0j8KHqRrEYC25mus/gviz/tq?gid=1104711743&range=A:T', 'https://docs.google.com/spreadsheets/d/1c9xyhR1_BavywsAOvpkf65Nhf9o0j8KHqRrEYC25mus/gviz/tq?gid=1552974580&range=A:C', 'https://docs.google.com/spreadsheets/d/1c9xyhR1_BavywsAOvpkf65Nhf9o0j8KHqRrEYC25mus/gviz/tq?gid=2111420909&range=A:E', 'https://docs.google.com/spreadsheets/d/1c9xyhR1_BavywsAOvpkf65Nhf9o0j8KHqRrEYC25mus/gviz/tq?gid=1379142381&range=A:F', 'https://docs.google.com/spreadsheets/d/1c9xyhR1_BavywsAOvpkf65Nhf9o0j8KHqRrEYC25mus/gviz/tq?gid=1749299608&range=A:D', 'https://docs.google.com/spreadsheets/d/1c9xyhR1_BavywsAOvpkf65Nhf9o0j8KHqRrEYC25mus/gviz/tq?gid=268750266&range=A:D', 'https://docs.google.com/spreadsheets/d/1c9xyhR1_BavywsAOvpkf65Nhf9o0j8KHqRrEYC25mus/gviz/tq?gid=148086622&range=A:C', 'https://docs.google.com/spreadsheets/d/1c9xyhR1_BavywsAOvpkf65Nhf9o0j8KHqRrEYC25mus/gviz/tq?gid=1474413858&range=A:C'];