First, sorry for my bad english.
I can create DBCont number charts in different div dynamicly. Like this:
$(function() {
$(document).ready(function() {
Highcharts.setOptions({
global: {
useUTC: false
}
});
for (var g = 0; g <= DbCount; g++)
{
var $container = $('<div id="div' + g + '" style="width: 400px; height: 200px;"></div>').appendTo(document.body);
new Highcharts.Chart({
chart: {
renderTo: $container[0],
type: 'areaspline',
marginRight: 10,
zoomType: 'x',
spacingRight: 20,
events: {
load: function() {
var series = this.series[0];
var series2 = this.series[1];
setInterval(function() {
var x = (new Date()).getTime(),
y1 = UsageGrafik[g],
y2 = UsageGrafik2[g];
if (y1 > 120 || y2 > 120 || isNaN(y1) || isNaN(y2)) {
y1 = 1;
y2 = 1;
}
series.addPoint([x, y1], false, true);
series2.addPoint([x, y2], true, true);
}, 5000);
....
You can see this is a dynamic chart.
My problem is setInterval(function()) sections are same for all charts. So all charts have same graphics. Because UsageGrafik[] array is change every 5 seconds in same page with ajax.
Basicly, i want create multiple charts with different setInterval(function()) section. Or another solution. Thank you
It's caused by closures. You have two options:
wrap creating chart into function: (function(g) { new Highcharts.Chart(...) })(g)
move out setInterval from definition:
new Highcharts.Chart({
chart: {
myIndex: g
renderTo: $container[0],
events: {
load: updateChart
}
},
...
});
function updateChart(e) {
var series = this.series[0];
var series2 = this.series[1];
var g = this.userOptions.chart.myIndex;
setInterval(function() {
var x = (new Date()).getTime(),
y1 = UsageGrafik[g],
y2 = UsageGrafik2[g];
if (y1 > 120 || y2 > 120 || isNaN(y1) || isNaN(y2)) {
y1 = 1;
y2 = 1;
}
series.addPoint([x, y1], false, true);
series2.addPoint([x, y2], true, true);
}, 5000);
....
}
Related
I'm trying to make linkable label to redirect to a page when clicked (i use this snippet: Add Link to X-Label Chart.js). I'm using the 2.9.4 version of ChartJS.
This is my code:
var ctx = document.getElementById("barChart-mostSold").getContext('2d');
this.chartmostSold =new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: {{listTopIssuerSoldLabel | tojson }},
datasets: [
{
data: {{listTopIssuerSoldValue | tojson }}
}
]
},
options: {
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove", "touchend"],
onClick: function(e, data) {
var ctx = $("#barChart-mostSold")[0].getContext("2d");
var base = this.chart.chartArea.bottom;
var height = this.chart.chart.height;
var width = this.chart.chart.scales['x-axis-0'].width;
var offset = $('#chartmostSold').offset().top - $(window).scrollTop();
if(e.pageY > base + offset){
var count = this.chart.scales['x-axis-0'].ticks.length;
var padding_left = this.chart.scales['x-axis-0'].paddingLeft;
var padding_right = this.chart.scales['x-axis-0'].paddingRight;
var xwidth = (width-padding_left-padding_right)/count;
// don't call for padding areas
var bar_index = (e.offsetX - padding_left - this.chart.scales['y-axis-0'].width) / xwidth;
if(bar_index > 0 & bar_index < count){
bar_index = Math.floor(bar_index);
console.log(bar_index);
}
}
}
}
});
I got this error:
Uncaught TypeError: Cannot read property 'top' of undefined
at tn.onClick ((index):1141)
at tn.handleEvent (VM64 Chart.min.js:7)
at tn.eventHandler (VM64 Chart.min.js:7)
at n (VM64 Chart.min.js:7)
at HTMLCanvasElement.Se.<computed> (VM64 Chart.min.js:7)
I recently updated Chart.js and got the following bug afterwards.
Some of the labels under my pie chart are not showing anymore, until I click on one of the visible ones. Then everything shows as it should.
Here you can see how it looks before clicking on something:
before clicking
and here you can see the chart after clicking on a visible label: after clicking
Here you can see the options section of my chart code:
options: {
maintainAspectRatio : false,
animation: {
duration: 0
},
elements: {
center: center
},
legend: {
position: 'bottom',
//fullWidth: 'true',
labels: {
generateLabels: (chart) => {
chart.legend.afterFit = function () {
var width = this.width;
this.lineWidths = this.lineWidths.map( () => this.width - 12 );
this.options.labels.padding = 30;
this.options.labels.boxWidth = 15;
};
var data = chart.data;
if (data.labels.length && data.datasets.length) {
return data.labels.map((label, i) => {
var meta = chart.getDatasetMeta(0);
var ds = data.datasets[0];
var arc = meta.data[i];
var custom = arc && arc.custom || {};
var getValueAtIndexOrDefault = this.getValueAtIndexOrDefault;
var arcOpts = chart.options.elements.arc;
var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
return {
text: label,
fillStyle: fill,
strokeStyle: stroke,
lineWidth: bw,
hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
// Extra data used for toggling the correct item
index: i
};
});
}
return [];
}
},
},
cutoutPercentage: 85,
plugins: {
datalabels: {
anchor: 'end',
align: 'end',
display: function(context) {
var index = context.dataIndex;
var value = context.dataset.data[index];
return value > 0; // display labels with a value greater than 0
},
font: {
weight: 'bold',
size: 16,
},
}
}
}
chart.legend.afterFit = function () {
if (keys.length > 4) {
this.options.labels.padding = 15;
this.height = this.height + 25;
}
else {
this.options.labels.padding = 20;
this.height = this.height + 18;
}
var width = this.width; // guess you can play with this value to achieve needed layout
this.lineWidths = this.lineWidths.map(function () { return width; });
};
I changed the part at afterFit to this, so it is formatting right, the problem is, the padding settings are set back when i click on a label.
I have an annotated line chart built successfully from a CSV file using google annotation charts. (Thanks Whitehat for your help).
I have looked unsuccessfully through the google chart examples to find a way of grabbing a slice of the line chart so as to then perform some calculations between the two points e.g. difference and percentage difference. There may be further calculations I wish to do but these two are enough for the moment.
Essentially I am trying to build a feature like Google's stock chart
Code so far:
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.71/jquery.csv-0.71.min.js"></script>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type='text/javascript'>
// load google charts
google.charts.load('current', {
packages: ['annotationchart']
}).then(function () {
// declare data variable
var arrayData;
// get csv data
$.get('test.csv', function(csvString) {
// get csv data success, convert to an array, draw chart
arrayData = $.csv.toArrays(csvString, {onParseValue: $.csv.hooks.castToScalar});
drawChart(arrayData);
})
});
// draw chart
function drawChart(arrayData) {
// convert string in first column to a date
arrayData = arrayData.map(function (row) {
return [new Date(row[0]),row[1],row[2]];
});
// create google data table, chart, and options
var data = google.visualization.arrayToDataTable(arrayData);
var chart = new google.visualization.AnnotationChart(document.getElementById('chart_div'));
var options = {
displayAnnotations: true
};
// draw chart
chart.draw(data, options);
}
</script>
</head>
<body>
<div id='chart_div' style='width: 1200x; height: 700px;'></div>
</body>
</html>
Any ideas how I can go about doing this?
you could use mouse events to allow the user to draw a selection on the chart.
given the coordinates of the selection,
use chart methods getChartLayoutInterface & getHAxisValue,
to determine the range of values the user selected.
see following working snippet,
click the chart and hold the mouse, then drag to draw the selection.
when the mouse is let go, the values selected will be displayed.
google.charts.load('current', {
packages: ['controls', 'corechart']
}).then(function () {
// build data table
var oneDay = (24 * 60 * 60 * 1000);
var dateEnd = new Date();
var dateStart = new Date(dateEnd.getTime() - (oneDay * 365.25));
var data = new google.visualization.DataTable();
data.addColumn('date', 'Date');
data.addColumn('number', 'Y');
for (var i = dateStart.getTime(); i <= dateEnd.getTime(); i = i + oneDay) {
var direction = (i % 2 === 0) ? 1 : -1;
var rowDate = new Date(i);
data.addRow([rowDate, rowDate.getFullYear() + (rowDate.getDate() * direction)]);
}
// chart options
var options = {
chartArea: {
height: '100%',
width: '100%',
top: 24,
left: 60,
right: 16,
bottom: 60
},
hAxis: {
format: 'MMM-yyyy'
},
height: '100%',
legend: {
position: 'top'
},
width: '100%'
};
// create chart and elements
var container = document.getElementById('chart');
var values = document.getElementById('values');
var chart = new google.visualization.LineChart(container);
// wait until chart is ready
google.visualization.events.addListener(chart, 'ready', function () {
// initialize variables
var chartLayout = chart.getChartLayoutInterface();
var chartArea = chartLayout.getChartAreaBoundingBox();
var chartBounds = container.getBoundingClientRect();
var select = document.getElementById('select');
var x1 = 0;
var y1 = 0;
var x2 = 0;
var y2 = 0;
var x3 = 0;
var y3 = 0;
var x4 = 0;
var y4 = 0;
// listen for mouse events
window.addEventListener('mousedown', function (e) {
select.className = '';
x1 = e.pageX;
y1 = e.pageY;
reCalc();
});
window.addEventListener('mousemove', function (e) {
if (select.className === '') {
x2 = e.pageX;
y2 = e.pageY;
reCalc();
}
});
window.addEventListener('mouseup', function (e) {
select.className = 'static';
selectPoints();
});
// show user selection
function reCalc() {
x3 = Math.min(x1,x2);
x4 = Math.max(x1,x2);
y3 = Math.min(y1,y2);
y4 = Math.max(y1,y2);
select.style.left = x3 + 'px';
select.style.width = x4 - x3 + 'px';
select.style.top = (chartBounds.top + chartArea.top + window.pageYOffset) + 'px';
select.style.height = (chartArea.height + window.pageYOffset) + 'px';
}
// show values from selection
function selectPoints() {
if ((((chartBounds.left + window.pageXOffset) <= x3) &&
((chartBounds.left + chartBounds.width + window.pageXOffset) >= x4)) &&
(((chartBounds.top + window.pageYOffset) <= y3) &&
((chartBounds.top + chartBounds.height + window.pageYOffset) >= y4))) {
var rows = data.getFilteredRows([{
column: 0,
minValue: chartLayout.getHAxisValue(x3),
maxValue: chartLayout.getHAxisValue(x4)
}]);
values.innerHTML = '';
rows.forEach(function (index) {
var value = values.appendChild(document.createElement('div'));
value.innerHTML = data.getFormattedValue(index, 0) + ': ' + data.getFormattedValue(index, 1);
});
}
}
});
// draw chart
chart.draw(data, options);
});
#select {
background-color: #3366cc;
border: 1px solid #3366cc;
opacity: 0.2;
position: absolute;
z-index: 1000;
}
.hidden {
display: none;
visibility: hidden;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div class="hidden" id="select"></div>
<div id="chart"></div>
<div id="values"></div>
I have created a chart using HighCharts and I want to disable any dataLabel if its width is more than height of its bar(bar cannot accomodate the dataLabel). I set the property 'enabled : false' of dataLabel of particular points but the effect is not being reflected in the chart.
<html>
<body>
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.1.1.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<div id="container" style="min-width: 100px; max-width: 400px; height: 400px; margin: 0 auto"></div>
<script language="javascript">
var pointSelected = {};
var pointHovered = {};
//Highcharts.chart('container',
var chartObject = {
chart: {
type: 'bar',
events : {
render : function(){
var ch = this;
var series = this.series;
ch.series.forEach(function(s){
s.points.forEach(function(point){
var barHeight = point.graphic.element.height.animVal.value;
var dataLabelWidth = point.dataLabel.width;
var plotBoxWidth = ch.plotBox.width;
console.log(plotBoxWidth);
if(barHeight + dataLabelWidth < plotBoxWidth) {
// console.log(barHeight + dataLabelWidth);
// console.log("point will lie inside");
}
//else{
if(dataLabelWidth > barHeight){
//USING JQUERY IT CAN BE DONE BUT I WANT TO AVOID JQUERY AS MUCH AS POSSIBLE $(point.dataLabel.element).fadeOut("fast");
point.dataLabel.alignOptions.enabled = false; //THIS IS WHERE I'M DISABLING POINT
console.log(point)
// point.update({dataLabels : {enabled : false}});
//ch.options.plotOptions.series.dataLabels.enabled = false;
}
if(barHeight + dataLabelWidth > plotBoxWidth){
// console.log(barHeight + dataLabelWidth);
var diff = barHeight + dataLabelWidth - plotBoxWidth;
// console.log(diff);
// var x = point.dataLabel.translateX;
// var y = point.dataLabel.translateY;
// console.log(x);
// console.log(point);
//// diff +=15;
var diff2 = barHeight - dataLabelWidth;
// console.log("diff2" + diff2);
point.dataLabel.translate( diff2 , point.dataLabel.alignAttr.y );
// console.log( point.dataLabel.text);
//point.dataLabel.stork("black");
// point.dataLabel.text.styles.fill = "black";
var elem = $(point.dataLabel.element).children();
$(elem).eq(0).css("fill" , "black");
// console.log(elem);
// $(textElem).attr("style" , "fill : black");
// console.log(textElem);
// $(point.dataLabel.element.innerHTML).eq(0).children().eq(0).text();
// console.log("point will lie outside");
}
// }
})
})
console.log(this);
}
}
},
title: {
text: 'Historic World Population by Region'
},
subtitle: {
text: 'Source: Wikipedia.org'
},
xAxis: {
categories: ['Africa', 'America', 'Asia', 'Europe', 'Oceania'],
title: {
text: null
}
},
yAxis: {
min: 0,
title: {
text: 'Population (millions)',
align: 'high'
}
},
tooltip: {
formatter : function(){
return '<b>' +this.series.name + '<br/>' +this.x + '<br/>' + this.y+ '000000</b>'
}
},
plotOptions: {
series : {
allowPointSelect : true,
dataLabels: {
enabled : true,
color : "blue",
crop : true,
overflow: "none"
},
point:{
events : {
select :function() {
//get the selected object
pointSelected.categories = this.category;
pointSelected.y = this.y;
console.log(this);
changeOpacity(pointSelected);
},
mouseOver : function(){
//get the hovered object
pointHovered.categories = this.category;
pointHovered.y = this.y;
changeOpacityOnHover(pointHovered);
},
mouseOut : function(){
//event handler when mouse moves out
changeOpacityOnOut(pointHovered);
}
}
}
}
},
legend: {
align: 'right',
verticalAlign: 'top',
layout: 'vertical',
x: -150,
y: 100,
},
credits: {
enabled: false
},
series: [{
name: 'Year 1800',
data: [10700, 45000, 45000, 20300, 20000],
zones : [{value : 100 , color : 'orange'} , {value : 500 , color : 'black'} , { color : 'blue'}]
}]
}
//debugger
var barChart = new Highcharts.chart('container', chartObject);
//function on mouseOver
function changeOpacityOnHover(pointHovered){
//get the current chart object
var chart = $("#container").highcharts();
//get the points and check each point whether it is the hovered one
chart.series.forEach(function(obj){
obj.data.forEach(function(datum){
//if hovered one then get its graphic element(rect) and change its opacity to 1
if(datum.category == pointHovered.categories && datum.y == pointHovered.y ){
// console.log(datum) ;
// console.log(datum.graphic.element);
var tag = datum.graphic.element;
var x = $(tag).attr("x");
// console.log(x);
$(tag).css("opacity" , "1");
}
});
})
}
function changeOpacityOnOut(pointHovered){
//get the current chart object
var chart = $("#container").highcharts();
//get the points and check each point whether it is the hovered one from which mouse is over
chart.series.forEach(function(obj){
obj.data.forEach(function(datum){
//get its graphic element(rect)
if(datum.category == pointHovered.categories && datum.y == pointHovered.y ){
// console.log(datum) ;
// console.log(datum.graphic.element);
var tag = datum.graphic.element;
var x = $(tag).attr("x");
// console.log(x);
//if the current point(hovered) is selected one OR no point is yet selected , opacity will be 1
if((pointHovered.categories == pointSelected.categories && pointHovered.y== pointSelected.y) || Object.keys(pointSelected).length == 0 )
{
$(tag).css("opacity" , "1");
}
//else change opacity to 0.1
else{
$(tag).css("opacity" , "0.1");
}
}
});
})
}
// if point is selected
function changeOpacity(pointSelected){
//get the current chart object
var chart = $("#container").highcharts();
//get the selected point by comparing each point to pointSelected
chart.series.forEach(function(obj){
obj.data.forEach(function(datum){
// if current point is selected point then change opacity to 1 and its color to the color of its rect tag fill attribute
if(datum.category == pointSelected.categories && datum.y == pointSelected.y){
console.log(datum) ;
// console.log(datum.graphic.element);
var tag = datum.graphic.element;
//var xVal = datum.graphic.element.x.animVal.value;
//var yVal = datum.graphic.element.y.animVal.value
//console.log(xVal);
//console.log(yVal);
var x = $(tag).attr("x");
//console.log(x);
// var x2 = xVal -1;
// console.log(datum.dataLabel.translate(xVal , yVal - 1));
//console.log("after");
// console.log(x2);
//console.log(yVal-1);
$(tag).css("opacity" , "1");
var color = $(tag).attr("fill");
$(tag).css("fill" , color);
// console.log(color + "when clicked");
}
//else let its opacity be 0.1
else{
var tag = datum.graphic.element;
$(tag).css("opacity" , "0.1");
}
});
})
}
</script>
</body>
</html>
I have solved the problem using jQuery but if it can be done simply by setting property that will be great. Also , why the effect is not being reflected if the property is set??
Thanks.
It should work with your configuration, this is how dataLabels should be configured:
plotOptions: {
bar: {
dataLabels: {
enabled: true,
crop: true,
overflow: 'none',
// inside: true // by default it's outside the bar
}
}
},
Demo: http://jsfiddle.net/pbjpr47t/
On my highchart i need a delay before the series tooltip is displayed.
I defined a new refresh function with a timer to realize it. If the timer is ready i check if the mouse position. If it moved not that much the tooltip should appear.
function (H) {
var timer = [];
var mousePosition = {
x: 0,
y: 0
};
window.addEventListener("mousemove", function (event) {
mousePosition.x = event.pageX;
mousePosition.y = event.pageY;
});
var getMousePositionX = function () {
return mousePosition.x;
};
var clearTimer = function () {
timer = [];
}
H.wrap(H.Tooltip.prototype, 'refresh', function (proceed) {
var mousePosX = getMousePositionX();
var delayForDisplay = this.chart.options.tooltip.delayForDisplay ? this.chart.options.tooltip.delayForDisplay : 1000;
timer[timer.length+1] = window.setTimeout(function () {
var currMousePosX = getMousePositionX();
if ((mousePosX >= currMousePosX - 5 && mousePosX <= currMousePosX + 5)) {
this.proceed.apply(this.tooltip, this.refreshArguments);
clearTimer();
}
}.bind({
refreshArguments: Array.prototype.slice.call(arguments, 1),
chart: this.chart,
tooltip: this,
clearTimer: clearTimer,
proceed: proceed
}), delayForDisplay);
});
};
The problem I have is, that the hover holos have also a delay.
Here is a sample: JSFiddle
Any solutions for this issue?
You can make new tooltip basing on your standard Highcharts tooltip and show it on your mouseover with some timeout:
load: function() {
chart = this;
this.myTooltip = new Highcharts.Tooltip(this, this.options.tooltip);
this.tooltip.label.element.remove();
}
point: {
events: {
mouseOver: function(e) {
var i = this.x;
points = [];
Highcharts.each(this.series.chart.series, function(s) {
Highcharts.each(s.data, function(p) {
if (p.x === i) {
points.push(p)
}
})
});
myTooltip = chart.myTooltip;
setTimeout(function() {
myTooltip.refresh(points, e)
}, 1000)
}, mouseOut: function() {
setTimeout(function() {
chart.myTooltip.hide();
}, 1000)
}
}
}
Here you can see an example how it can work: http://jsfiddle.net/az39das8/