I've been tinkering with a jqplot graph where the grid lines are on top (or in front depending how you look at it) of the graph as opposed to the default background area. I've made several attempts to get it to overlay the grid using the z-index. However, each attempt renders the entire graph non-functional and I receive no error for some unknown reason.
I'm working with a stacked bar chart that actually fills the entire grid, so I don't get to see any of the grid lines, they are all hidden beneath (or behind) the graph.
Here is the code:
<script type="text/javascript" language="javascript">
$.jqplot.config.enablePlugins = true;
var plot;
var data1 = [];
var data2 = [];
var index = 0;
var num = 0;
var delta = 0;
$(document).ready(function(){
for (i=0; i<100; i++) {
num = getRandomNumber();
delta = 100 - num;
index++;
data1.push([ index, num]);
data2.push([ index, delta]);
}
plot = $.jqplot('graph', [data1, data2],{
title: 'my title',
animate: true,
stackSeries: true,
seriesDefaults:{
renderer:$.jqplot.BarRenderer,
rendererOptions: { highlightMouseDown: true },
pointLabels: {show: true}
},
series: [ {label: 'one'}, {label: 'two'} ],
seriesColors:['#ff0000', '#0000ff'],
legend: {
show: true,
location: 'e',
placement: 'outsideGrid'
},
grid: {
gridLineColor: '#333333',
borderWidth: 0
},
axesDefaults: {
pad: 0,
padMin: 0
},
axes: {
xaxis: {
showTicks: false,
pad: 0,
padMin: 0,
rendererOptions: { forceTickAt0: true, forceTickAt100: true }
},
yaxis: {
pad: 0,
padMin: 0,
rendererOptions: { forceTickAt0: true, forceTickAt100: true }
}
}
});
});
getRandomNumber = function(){
return Math.floor(Math.random()* 100);
};
</script>
Anyone run into this requirement and know how to get the grid lines to show up on top of the graph? Thanks
With a little DOM manipulation you can do this BUT you need to make sure to set the grid background color transparent for it to work. After your plot call:
gridCanvas = $($('.jqplot-grid-canvas')[0])
seriesCanvas = $($('.jqplot-series-canvas')[0])
gridCanvas.detach();
seriesCanvas.after(gridCanvas);
Here's a sample fiddle.
Related
i have a big data set to display ( around 150 category) with a library named vue-apexchart my problem is that i cant find a way to make the graph scrollable on xaxis without having a broken display the following image will explain my problem better
as you can see having too many categories leads to an unreadable chart
can you please help me
i have tried to make a scrollable div, the probleme still remains ( unreadable xaxis )
here's my chartOptions object :
chartOptions: {
chart: {
type: "bar",
height: 350,
},
plotOptions: {
bar: {
horizontal: false,
columnWidth: "55%",
endingShape: "rounded",
},
},
dataLabels: {
enabled: false,
},
stroke: {
show: true,
width: 2,
colors: ["transparent"],
},
xaxis: {
categories: [],
},
fill: {
opacity: 1,
},
},
I think there are two strategies that you could adopt here. In my opinion, the second one is better.
1. Horizontal bar chart
If you do not have strict constraints in terms of UX/UI, a possible solution might be to switch both axes...
plotOptions: {
bar: {
horizontal: true
}
},
... and give your chart much more height:
chart: {
type: 'bar',
height: 2500,
},
Doing that would allow you to use the natural vertical scrolling of your browser.
let data = [];
for (let i = 1; i <= 150; i++) {
data.push({x: `Category ${i}`, y: i});
}
let options = {
series: [{
name: 'Series',
data
}],
chart: {
type: 'bar',
height: 2500
},
yaxis: {
labels: {
style: {
fontSize: '10px'
},
offsetY: 3
}
},
plotOptions: {
bar: {
horizontal: true
}
},
dataLabels: {
enabled: false
}
};
let chart = new ApexCharts(document.querySelector('#chart'), options);
chart.render();
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<div id="chart"></div>
All categories are visible. This is not very handy, though.
2. Zoom, pan and tick settings
Zooming and panning are useful features. They are available by default in the toolbar, but this toolbar does not appear immediately when your xaxis has categories. To enable the toolbar in this case, you have to change xaxis.tickPlacement from 'between' to 'on' (see Zoom on Category Bar Charts – ApexCharts.js).
Since you have a lot of categories, it is also a good idea to limit the number of visible labels with xaxis.tickAmount.
xaxis: {
tickPlacement: 'on',
tickAmount: 25
},
Thanks to the zoomX() method, you can decide to display your chart with only a subset of your data. The rest is still accessible via zooming and/or panning.
let data = [];
for (let i = 1; i <= 150; i++) {
data.push({x: `Category ${i}`, y: i});
}
let options = {
series: [{
name: 'Series',
data
}],
chart: {
type: 'bar',
height: 350
},
xaxis: {
tickPlacement: 'on',
tickAmount: 25
},
dataLabels: {
enabled: false
}
};
let chart = new ApexCharts(document.querySelector('#chart'), options);
chart.render();
chart.zoomX(37, 71);
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<div id="chart"></div>
When i draw a chart with 4 lines, each with its own data ofc, i programmatically create the options for the LineChart that has 4 Yaxis, first one on the left and the rest on the right side. Now, after the chart is drawn and i de-select some datasources from the list (less lines to draw), the now-obsolete yAxis ticks stay there, even when the chart correctly draws only the selected lines, and the options are updated as well correctly. I cant think of a way to remove them!
I have googled for 2 days and cant find a solution. I am using react in functional style and it makes things more complicated because every advice seems to be in the classic style.
I am using react-chartjs-2 wrapper as well, if this helps.
I am also quite new to react, and asking in Stackoverflow, so please cut me some slack :)
I assume the chart is being re-rendered or something because the amount of lines etc do change.
In the images, the "createYaxis" that is shown in the console.log is the generated yAxes- part of the options object (which is functional otherwise). The problem yAxises are on the right side in red and yellow. Images show before and after situation.
Image of the options-object generated by the code below the img:
var yAxisItems = [];
function createYaxises (num){
var arr = [];
for (var i=0;i<num.length;i++){
if (i===0){
arr.push({
display: true,
id: i,
type: 'linear',
position: 'left',
gridLines: {
display:false,
//color: 'blue'
},
ticks: {
fontColor: lineColourArray[i],
fontSize: 14,
}
})
}
else {
arr.push({
display: true,
id: i,
type: 'linear',
position: 'right',
gridLines: {
display:false,
//color: 'blue'
},
ticks: {
display:true,
fontColor: lineColourArray[i],
fontSize: 14,
}
})
}}
yAxisItems = arr;
console.log("createyaxis arr: " , arr);
console.log("createyaxis: " , yAxisItems); //JSON.stringify(yAxisItems));
}
//get data for selected sensors and set it to chart data
const handleGetSelectedSensorData = function () {
var d = getSelectedSensorData();
console.log("d: ", d);
var dSets = [];
if (d[0]){
d.map((dItem,index)=> {
var newDsetData =[];
if (dItem.data){
dItem.data.map((innerDataItem)=> {
var dSet = {};
dSet.x = innerDataItem.timestamp;
dSet.y = innerDataItem.v;
newDsetData.push(dSet);
})
var newset = {
data: newDsetData,
label: dItem.sensorTag,
borderColor: lineColourArray[index],
fill: false,
pointRadius: 1.5,
backgroundColor:lineColourArray[index],
borderWidth: 2,
showLine: true,
pointHoverRadius: 5,
lineTension: 1,
};
dSets.push(newset);
}})
var dDataTemp = {};
var optionsTemp = new Object();
dDataTemp.datasets =dSets;
//create yaxises only once
createYaxises(dDataTemp.datasets);
//more than one set (TODO)
//console.log("dDataTemp.datasets : ", dDataTemp.datasets)
if (dDataTemp.datasets.length >1){
console.log("dset > 1");
for(var i=0;i< dDataTemp.datasets.length;i++) {
dDataTemp.datasets[i].yAxisID = i;
console.log("setting options");
optionsTemp ={
tooltips: {
enabled: true,
intersect:false,
mode:'x',
callbacks: {
title: function(tooltipItem, data) {
var toSplit = tooltipItem[0].label.split(",");
return (toSplit[0]);
},
label: function (tooltipItem) {
var split = tooltipItem.xLabel.split(',');
//return ( Number(tooltipItem.yLabel).toFixed(3));
return (split[2] + " : " + Number(tooltipItem.yLabel).toFixed(3));
}
},
},
hover: {
mode: 'nearest',
intersect: true,
},
title:{
display:true,
text:'Valittu sensoridata',
fontSize:20
},
legend:{
display:true,
position:'right'
},
scales: {
xAxes: [{
display: true,
type: 'time',
ticks: {
}
}],
yAxes:
yAxisItems
}
}
}
setOptions(optionsTemp);
console.log("options: " , optionsTemp);
setdData(dDataTemp);
}}
else {
console.log("error in handleGetSelectedSensorData()");
}
}
And the Line is just added like this:
<Line data={dData} options = {options} />
Instead of setting display: true set display: 'auto', this will make the axis dissapear as long as there is no dataset visable that is linked to that scale, as soon as a dataset becomes visable that is linked to that scale it will show the scale again.
Doc: https://www.chartjs.org/docs/master/axes/cartesian/#common-options-to-all-axes
I am using the chart js to display a bar graph. It's working correctly on normal instances, but I am willing to change the color or a small portion of the label i.e, I want to include some HTML on the label of the bar chart. But, it isn't rendering the HTML instead it is showing plain HTML text.
If it is not possible, it's okay for me if there is another way to achieve this like, change the color of the price and keep the name as it is.
let $js_dom_array = ["43.28", "93.13"];
let $js_am_label_arr = ["<span>$0</span> None", "<span class='text-danger'>$23.63</span> Handicap Accessible"];
let ctx2 = document.getElementById("barChart").getContext("2d");
let chart = new Chart(ctx2, {
type: 'bar',
data: {
labels: $js_am_label_arr,
datasets: [{
label: 'Amenity Name',
data: $js_dom_array,
backgroundColor: 'rgba(26,179,148,0.5)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
legendCallback: function(chart) {
var text = [];
for (var i=0; i<chart.data.datasets.length; i++) {
console.log(chart.data.datasets[i]); // see what's inside the obj.
text.push(chart.data.datasets[i].label);
}
return text.join("");
},
tooltips: {
"enabled": false
},
scales: {
xAxes: [{
stacked: false,
beginAtZero: true,
ticks: {
stepSize: 1,
min: 0,
autoSkip: false,
callback: function(label, index, labels) {
if (/\s/.test(label)) {
return label.split(" ");
}else{
return label;
}
}
}
}]
},
animation: {
duration: 0,
onProgress: function() {
var chartInstance = this.chart,
ctx = chartInstance.ctx;
ctx.font = Chart.helpers.fontString(16, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
this.data.datasets.forEach(function(dataset, i) {
var meta = chartInstance.controller.getDatasetMeta(i);
meta.data.forEach(function(bar, index) {
if (dataset.data[index] > 0) {
let data = dataset.data[index];
ctx.fillText('$'+Math.round(data), bar._model.x, bar._model.y - 5);
}
});
});
}
},
}
});
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<canvas id="barChart" height="140"></canvas>
</div>
#Note: Here, you might see that the data in $js_am_label_arr is already an HTML element, but if there is something from where I could pass the array of the raw values and the convert in HTML than I could pass the raw value (without) as well.
Currently $js_am_label_arr is created as:
if($avg_amount < 0){
$text_color = 'text-danger';
$avg_amount_text = "<span class='text-danger'>$".abs($avg_amount)."</span>";
}else{
$text_color = '';
$avg_amount_text = "<span>$".abs($avg_amount)."</span>";
}
$am_label_arr[] = $avg_amount_text.' '.$fv['amenity_name'];
Update:
Expected Output
So if the value is negative for example in the above case, its -$23.63. In this case, I want the label to be ($23.63)[in color red] followed by the name Handicap Accessible. This can be seen at the result as well, text-danger classes is added to show that part in red color.
As you are open to any plugin so i suggest you to use HighCharts to achieve above case . In below demo code i have just passed the label value to categories in xAxis and done a little change to span tag . i.e : i have added inline css to span where you need to display color red .
Here is demo code :
var chart;
let $js_dom_array = [43.28, 93.13];
let $js_am_label_arr = ["<span>$0</span> None", "<span style='color:red'>$23.63</span> Handicap Accessible"];
$(document).ready(function() {
chart = new Highcharts.Chart({
chart: {
renderTo: 'chart_container', //under chart_container chart will display
defaultSeriesType: 'bar', //bar grapgh
backgroundColor: '#CCCCCC',
type: 'column' //to display in columns wise
},
plotOptions: {
bar: {
colorByPoint: true,
dataLabels: {
enabled: false
}
}
},
title: {
text: 'Something.... '
},
xAxis: {
categories: $js_am_label_arr, //for value in labels
},
series: [{
name: 'Amenity Name',
data: $js_dom_array //array value to plot data
}]
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.highcharts.com/gantt/highcharts-gantt.js"></script>
<div id="chart_container"></div>
You can change the color when you hover the label, with the tooltips callback
Move your mouse over the bar
let $js_dom_array = [43.28, 93.13];
let $js_am_label_arr = ["$0", "$23.63"];
let ctx2 = document.getElementById("barChart").getContext("2d");
let chart = new Chart(ctx2, {
type: 'bar',
data: {
labels: $js_am_label_arr,
datasets: [{
backgroundColor: 'rgba(26,179,148,0.5)',
label: 'Amenity Name',
data: $js_dom_array,
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
tooltips: {
enable: true,
callbacks: {
labelTextColor: function(tooltipItem, chart) {
if(tooltipItem.index === 1)
return 'red';
}
}
},
scales: {
xAxes: [{
stacked: false,
beginAtZero: true,
ticks: {
stepSize: 1,
min: 0,
autoSkip: false,
fontColor: "red",
callback: function(label, index, labels) {
if (/\s/.test(label)) {
return label.split(" ");
}else{
return label;
}
}
}
}]
}
}
});
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<canvas id="barChart" height="140"></canvas>
</div>
As far as i know, its not possible to do what you want.
With the current version (v2.9.3) its not even possible to change the color for specifics X ticks labels, you can only change the color for every label with:
options: {
scales: {
yAxes: [{
ticks: {
fontColor: "red",
}
}],
xAxes: [{
ticks: {
fontColor: "red",
}
}]
}
}
There is a workaround with version v2.6.0 (as you tagged this version, i imagine you are using it) wich you can pass an array of colors to fontColor, like: fontColor: ["red","green"], but you need to change some code lines of chartjs and yet you cannot change just a specific part of the text of axis tick label as you want.
If you have interested in this solution, you can check it here Add possibility to change style for each of the ticks
But again, this solution is related to an older version of chartjs and i dont know what features you lose here.
Looks like it will be able to change the color for each ticks in version 3.0, but this version isnt released yet.
UPDATED
I done a simple example here with c3/d3 js. but it has some points:
It take a little time to show Labels updated when chart is rendered.
On changeTickLabel i did a harded code to check value and then append the text (like 'Handicap Accessible'). So here you will need to find logic that is better to you.
var chart = c3.generate({
onrendered: function () { changeTickLabel() },
data: {
columns: [
['data1', 43.28, 93.13]
],
type: 'bar',
},
axis: {
x : {
type: 'category',
categories: ["$0", "$23.63"],
tick: {
multiline:false,
culling: {
max: 1
},
},
},
}
});
function changeTickLabel(){
d3.selectAll('.c3-axis-x .tick tspan')
.each(function(d,i){
var self = d3.select(this);
var textValue = self.text();
if(textValue !== '$0'){
self.style("fill", "red");
self.append('tspan').style("fill", "black").text(' Handicap Accessible');
}
else {
self.append('tspan').text(' None');
}
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.16.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.20/c3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="chart"></div>
I've merged positive and negative data to one data set called yAxis (multiple columns for both positive and negative). Here's the code:
//...
var yAxis = [wololoTotalValues];
var xAxis = wololoTotalKeys;
window.onload = function () {
Flotr.draw(document.getElementById("chart"), yAxis, {
title: "wololo",
bars: {
show: true,
barWidth: 0.8
},
yaxis: {
min: 0,
max: wololoMaxValue + 10,
tickDecimals: 0
},
xaxis: {
ticks: xAxis
}
});
};
//...
<div id='chart' style="width:1200px;height:500px;"></div>
I would like 'bad' and 'ble' to be red. I found some manuals how to handle this problem with flot, but not flotr(flotr2).
Is there any way to make it somehow like below? Or maybe I must split the data like here?
colors: (yAxis[0-lastGood] : "#0000FF"), (yAxis[lastGood+1-lastBad] : "#FF0000")
Okay, found the solution, splitting the data into two arrays helped. Code is below:
var gud = [];
for(i=0; i<wololoLengthGood; i++){
gud.push([i, wololoValuesGood[i]]);
}
var bad = [];
for(i=wololoLengthGood; i<wololoLengthGood+wololoLengthBad; i++){
bad.push([i, wololoValuesBad[i-wololoLengthGood]]);
}
window.onload = function () {
Flotr.draw(document.getElementById("chart"),
[{ // replacement of 'yAxis' from here
data: gud,
color: "#0000FF"
},{
data: bad,
color: "#FF0000"
}], // till here.
//...
I'm using Highcharts to draw a spline. This works good.
I'm using secondary series (columns) for another data.
My problem is: how to set column height to be matched with spline.
As far i've done this: jsfiddle
Columns have height almost i need, but not all.
It is possible at all?
Relevant code:
$('#chart').highcharts({
title: {text: 'Chart'},
chart: {
type: 'spline'
},
xAxis: {
title: {
text: 'X-Axis'
}
},
yAxis: {
title: {
text: 'Y-Axis'
},
labels: {
enabled: false
}
},
series: [{
name: 'Chart',
data: chdata,
marker: {
enabled: false
},
}],
legend: {
enabled: false
}
});
var chartObj = Highcharts.charts[$('#chart').attr('data-highcharts-chart')];
var d = [];
for(var i = 0; i < ldata.length; i++) {
d.push([ldata[i],getYValue(chartObj,0,getClosest(ldata[i],bdata[0]))]);
}
chartObj.addSeries({
type: 'column',
pointWidth: 1,
borderWidth: 0,
data: d
});
I've tried even with plotLines, but it have height of whole chart.
Thanks,
Bartek
Rather than plotting the height of the closest point, try plotting the average height of the points either side of your bar.
You can get even closer if you work out a linear best fit for your bar on the line between the points either side. Something like:
function getY(xVal,points) {
var higher = 0;
var lower=0;
for(var i=0;i<points.length;i++){
if (points[i][0] > xVal) {
higher=i;
lower=i-1;
break;
}
}
return points[lower][1] + ((points[higher][1]-points[lower][1])*(xVal-points[lower][0])/(points[higher][0]-points[lower][0]));
};
http://jsfiddle.net/5h471o3d/