I'm using chart.js, and it's quite handy, but I'm facing an unexpected behavior when I click on arabic. The values change correctly, but if I hover over the bars, it shows the english values, although both of Pages, and views array have the arabic values.
var Pages = [];
var viewers = [];
var keys = [];
var countryvalue = [];
var lang = 'english';
function fillArray(language) {
if (language == 'english') {
Pages = ['home', 'about', 'contact'];
viewers = [5, 2, 3];
} else if (language == 'arabic') {
Pages = ['arabic home', 'arabic about', 'arabic contact'];
viewers = [7, 1, 2];
}
}
function getPages(lang) {
Pages = [];
viewers = [];
fillArray(lang);
drawBar();
}
function drawBar() {
var randomScalingFactor = function() {
return Math.round(Math.random() * 100)
};
var barChartData = {
labels: Pages,
datasets: [{
fillColor: "rgba(151,187,205,0.5)",
strokeColor: "rgba(151,187,205,0.8)",
highlightFill: "rgba(151,187,205,0.75)",
highlightStroke: "rgba(151,187,205,1)",
data: viewers
}]
}
var ctx = document.getElementById("canvas").getContext("2d");
window.myBar = new Chart(ctx).Bar(barChartData, {
responsive: true
});
}
getPages(lang);
$('.languageSwitcher').on('click', function(e) {
e.preventDefault();
if ($(this).data('lang') != lang) {
lang = $(this).data('lang');
getPages(lang);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js"></script>
<div class="barContainer">
<canvas id="canvas"></canvas>
</div>
<a class="languageSwitcher" data-lang="english">English</a>
<a class="languageSwitcher" data-lang="arabic">Arabic</a>
Here is a fiddle that shows the problem
You need to destroy the existing chart before creating the new one. So something like
var ctx = document.getElementById("canvas").getContext("2d");
if (window.myBar)
window.myBar.destroy();
...
You could also do a similar thing by updating the points and calling update(), but destroy() is easier by far.
Fiddle - https://jsfiddle.net/jzq5umfm/
Note : while removing the existing canvas element and adding a new one would also seemingly work (as mentioned in your comment and the previous version of your question), with responsive: true, Chart.js cycles through all created instances of the graph to resize them, resulting in a console error for each responsive graph that has been created and removed but not destroyed.
For responsive: false you won't see console errors but you'd still have references to those unused instances.
Related
I'm trying to figure out chartjs' destroy method and it's not working quite right. I'm doing this on dummy data with a dummy button, but here's what I have.
Really simple html markup for testing:
<div class="container mt-5">
<div class="row">
<canvas id="myChart"></canvas>
</div>
<div class="row mt-5">
<button class="btn btn-primary" id="chartBTN">Next Chart</button>
</div>
</div>
Simple js below. The point is that a chart is generated on load, then, on button click, the chart adds another data set to it for comparison. To do that, I understand I have to destroy the first chart to recreate the second. Testing the button click works, but the chart destroy doesn't do anything. Instead, I get an error.
Am I putting the destroy method in the wrong place?
// Our labels along the x-axis
var years = [1500,1600,1700,1750,1800,1850,1900,1950,1999,2050];
// For drawing the lines
var africa = [86,114,106,106,107,111,133,221,783,2478];
var asia = [282,350,411,502,635,809,947,1402,3700,5267];
var europe = [168,170,178,190,203,276,408,547,675,734];
var latinAmerica = [40,20,10,16,24,38,74,167,508,784];
var northAmerica = [6,3,2,2,7,26,82,172,312,433];
var ctx = $("#myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: years,
datasets: [
{
data: africa
}
]
}
});
$('#chartBTN').on('click', function(){
myChart.destroy();
var ctx = $("#myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: years,
datasets: [
{
data: africa,
label: 'Africa',
borderColor: "#3e95cd",
fill: false
},
{
data: asia,
label: "Asia",
borderColor: "#3e95cd",
fill: false
}
]
}
});
});
A variable declared with var is hoisted to the top of the function. You are declaring the same variable again in the function.
So the function declaration is hoisted to top of function where it is undefined.
Hence, myChart.destroy() is undefined.
https://developer.mozilla.org/en-US/docs/Glossary/Hoisting
Instead of actually destroying the instance, you should update the instance instead.
myChart.data.datasets = [] //Desired Data
myChart.update();
If you want to go with destroying the instance, you can remove the var declaration from inside the function and it should work fine. (since the variable is already defined in scope of that function.
I am using angular-chart.js
<div class="container" ng-controller="LineCtrl">
<canvas id="bar" class="chart chart-bar" data="barChartData" options="options" series="lineChartSeries"
colours="colours" legend="true" labels="barChartLabel"></canvas>
</div>
css for this chart is
.chart #bar {
width: 100%!important;
height: 300px;
}
but chart is showing with overlapping labels like
That's because Chart.js shows all x axis labels and doesn't drop them if the overlap. There is a feature request out there for this https://github.com/nnnick/Chart.js/pull/897 - which is still open.
There is also a separate branch - https://github.com/nnnick/Chart.js/pull/521 which solves the issue. But, it hasn't been integrated into the main branch yet and you might want to read the full thread before opting for this.
There is a workaround however. You can just opt to set everything except every nth label when you pass in the labels array, like this
labels: ["Jan 1", "Jan 2",... and lots of days ....].map(function (e, index) {
return index % 5 ? "" : e;
})
Adjust the value 5 as needed. The best way would be to adjust this based on the size of the array to get x number of points or less if the number of points crosses a threshold. e.g. if you want no more than 10 points, replace 5 with size of array / 10 IF size of array > 200 or at whichever point the overlaps start. Just make sure you don't end up with too less markers :-). For instance, if you pick 30 instead of 200 as the threshold, there will be times when you have only 3 markers for 30+ data points. Or you can choose to be more creative with your mapping function (for example, make sure that you have a label at the end of the scale, etc.)
Here is an example with just Chart.js. The same logic will work with angular-chart.js as well.
var myLabels = []
var myValues = []
for (var i = 0; i < 1000; i++) {
myLabels.push("label" + i);
myValues.push(Math.random() * 1000)
}
var data1 = {
labels: myLabels,
datasets: [{
label: "My Dataset",
fillColor: "rgba(244, 6, 6, 1)",
data: myValues
}]
};
var ctx = document.getElementById("chart1").getContext("2d");
window.weeksChart = new Chart(ctx).Bar(data1, {
barShowStroke : false
});
var data2 = {
labels: myLabels.map(function (e, index) {
return index % 30 ? "" : e;
}),
datasets: [{
label: "My Dataset",
fillColor: "rgba(244, 6, 6, 1)",
data: myValues
}]
};
var ctx = document.getElementById("chart2").getContext("2d");
window.weeksChart = new Chart(ctx).Bar(data2, {
barShowStroke : false
});
with HTML
<canvas id="chart1" width="651" height="335"></canvas>
<canvas id="chart2" width="651" height="335"></canvas>
And here is the corresponding fiddle - http://jsfiddle.net/2kvwndtq/
The downside is that if you want tooltips you have to override your tooltip function as well to show the spaces with the corresponding label and not blank.
I am new to client-side programming. Thus far I've been writing only asp and php based solutions. But now I need to retrieve data from json and plot on a map (I don't know how to do that yet, but this is later).
After days of searching, I think OpenLayers can give me what I need.
I have gone through the Examples on dev.openlayers site, (such as this one http://dev.openlayers.org/releases/OpenLayers-2.13.1/examples/vector-features-with-text.html), and also searched (and found some) solutions on stackoverflow, but they don't offer solutions to my problems).
Please view what I've done so far:
http://www.nusantech.com/bangkuujian/openlayer.html
The canvas.js is as follows:
// create some sample features
var Feature = OpenLayers.Feature.Vector;
var Geometry = OpenLayers.Geometry;
var features = [
new Feature(new Geometry.Point(-220, -60),attributes = { name: "Mercury",align: "cm",xOffset:10,yOffset:50 }),
new Feature(new Geometry.Point(-70, 120),attributes = { name: "Venus" }),
new Feature(new Geometry.Point(0, 0),attributes = { name: "Earth" }),
new Feature(new Geometry.Point(160, -100),attributes = { name: "Mars",align: "cm",xOffset:10,yOffset:50 })];
// create rule based styles
var Rule = OpenLayers.Rule;
var Filter = OpenLayers.Filter;
var style = new OpenLayers.Style({
pointRadius: 10,
strokeWidth: 3,
strokeOpacity: 0.7,
strokeColor: "#ffdd77",
fillColor: "#eecc66",
fillOpacity: 1,
label : "${name}",
fontColor: "#f0f0f0",
fontSize: "12px",
fontFamily: "Calibri, monospace",
labelAlign: "${align}",
labelXOffset: "${xOffset}",
labelYOffset: "${yOffset}",
labelOutlineWidth : 1
},
{
rules: [
new Rule({
elseFilter: true,
symbolizer: {graphicName: "circle"}
})
]
});
var layer = new OpenLayers.Layer.Vector(null, {
styleMap: new OpenLayers.StyleMap({'default': style,
select: {
pointRadius: 14,
strokeColor: "#e0e0e0",
strokeWidth: 5
}
}),
isBaseLayer: true,
renderers: ["Canvas"]
});
layer.addFeatures(features);
var map = new OpenLayers.Map({
div: "map",
layers: [layer],
center: new OpenLayers.LonLat(50, 45),
zoom: 0
});
var select = new OpenLayers.Control.SelectFeature(layer);
map.addControl(select);
select.activate();
What I have problems with:
Label offset
In the samples, the labels should offset from the centre by labelXOffset: "(xvalue)", labelYOffset: "(yvalue)", but this is not happening in my page. Is there something I forgot?
Zoom-in
When I click the + button on the map, all the features look like they are zoomed in, however, the sizes of the features stay the same. How do I enlarge the features (circles) too?
Hit Detection
i) When I click on a circle, it is selected as designed. However, is it possible when I select a circle, I also change the right side (now there is a red "text here") and fill it up with html? Can you show me an example how to change the red "text here" to the label-name of the selected circle with a different colour?
ii) Secondly, after I select a circle, how do I add a label under all the other circles denoting the distance between each circle and the selected circle?
Thank you in advance, hopefully these questions are not too much.
I have another question about retrieving an array of coordinates from json to plot the circles, but I will do more research on that. If you can point me in the right direction with regards to this, it would be much appreciated too.
I know how to do them server-side asp or php, but client side is very new to me. However client-side can do all of this much-much faster and can reduce a lot of load.
Cheers,
masCh
I think I have managed to most of it.
Labels not offsetting
Not sure what I did, but I declared a WMS layer and made a few changes to offset and now it is offsetting correctly.
var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic",
"http://hendak.seribudaya.com/starmap.jpg",
{
layers: "modis,global_mosaic",
}, {
opacity: 0.5,
singleTile: true
});
var context = {
getSize: function(feature) {
return feature.attributes["jejari"] / map.getResolution() * .703125;
}
};
var template = {
pointRadius: "${getSize}", // using context.getSize(feature)
label : "\n\n\n\n${name}\n${jarak}",
labelAlign: "left",
labelXOffset: "${xoff}",
labelYOffset: "${yoff}",
labelOutlineWidth : 0
};
var style = new OpenLayers.Style(template, {context: context});
And I declared xoff & yoff under new OpenLayers.Geometry.Point(x,y), { jejari:5, xoff: -10, yoff: -15 }
2) Zoom in on point features.
This was a weird problem. Anyway, I declared a radius called jejari as in the code above next to xoff and yoff. Then modified pointRadius from a static number to "${getSize}" And then added the getSize function to var template which retrieves the current radius. I think that was all I did for that. But the labels were running all over the place, I still haven't solved that.
3) Hit detection and changing another in html
This adds what happens to the once a point feature has been selected
layer.addFeatures(features);
layer.events.on({ "featureselected": function(e) {
kemasMaklumat('maklumat', "<FONT FACE='Calibri' color='#f0f0f0' size=5><center>"+
e.feature.attributes.name+
"<p>This is displayed text when a feature has been selected";
maklumat.style.color="black";
layer.redraw();
}
});
map.addLayers([layer]);
And in the html the and the kemasMaklumat function is declared as
<script type="text/javascript">
function kemasMaklumat(id,content) {
var container = document.getElementById(id);
container.innerHTML = content;
}
</script>
<td valign="top"><div id="maklumat" style="border-radius:25px; background-color:#000000;box-shadow: 8px 8px 4px #686868;">
Write Something Here<P>
</div></td>
The second part of this question was changing the labels of all the UNselected features, i.e. modifying attributes of all features that weren't the selected one. To do this, I added a for loop through all the features and check if it has the same label as the feature that was selected, this was done under the layer.events.on "featureselected" as was done in the above part 1 of this question.
layer.addFeatures(features);
layer.events.on({ "featureselected": function(e) {
kemasMaklumat('maklumat', "<FONT FACE='Calibri' color='#f0f0f0' size=5><center>"+
e.feature.attributes.name+
"<p>This is displayed text when a feature has been selected";
maklumat.style.color="black";
for (var i = 0, l = layer.features.length; i < l; i++) {
var feature = layer.features[i];
if (feature.attributes.name!=e.feature.attributes.name) {
feature.attributes.name="I was not selected"; }}
layer.redraw();
}
});
map.addLayers([layer]);
I am trying to create a cycle/slider that has two google charts in it. I can display them separately on the page with the below code, but when space became premium I decided to go to slider/cycle. With the below code. The first chart draws but when the second scrolls into view. There is no chart. It only says: Unable to get property 'length' of undefined or null reference on Chrome: Cannot read property 'length' of null. I realize that when one chart is visible the other is not. But my unfamiliarity with javascript is making it hard to come up with an answer that will turn display:'block' to display:'none at the appropriate times and back. Any help would be much appreciated.
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
function drawCharts() {
var data1 = google.visualization.arrayToDataTable(<% =jsostring%>);
var options1 = {
title: 'PSNL Weight Chart',
backgroundColor: {fill:'none'}
};
var data2 = google.visualization.arrayToDataTable(<% =jsostring2%>);
var options2 = {
title: 'PSNL Sleep Chart',
backgroundColor: {fill:'none'}
};
var chartA = new google.visualization.LineChart(document.getElementById('chart_div'));
chartA.draw(data1, options1);
var chartB = new google.visualization.ColumnChart(document.getElementById('chart_div2'));
chartB.draw(data2, options2);
}
google.setOnLoadCallback(drawCharts);
google.load("visualization", "1", { packages: ["corechart"] });
</script>
I guess the answer is very basic. I'm not a java person. I searched and thought about it. The answer, which I found on another site is:
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
function drawCharts() {
var data1 = google.visualization.arrayToDataTable(<% = jsostring%>);
var options1 = {
title: 'PSNL Weight Chart',
backgroundColor: {fill:'none'}
};
var data2 = google.visualization.arrayToDataTable(<% = jsostring2%>);
var options2 = {
title: 'PSNL Sleep Chart',
backgroundColor: {fill:'none'}
};
var chartA = new google.visualization.LineChart(document.getElementById('chart_div'));
document.getElementById('chart_div').style.display = 'block';
document.getElementById('chart_div2').style.display = 'none';
chartA.draw(data1, options1);
var chartB = new google.visualization.ColumnChart(document.getElementById('chart_div2'));
document.getElementById('chart_div2').style.display = 'block';
document.getElementById('chart_div').style.display = 'none';
chartB.draw(data2, options2);
}
google.setOnLoadCallback(drawCharts);
google.load("visualization", "1", { packages: ["corechart"] });
</script>
As the jquery scroll through. It will turn on the first Div_Chart and turn off the other before it scrolls into view.
I am using Jquery Flot to create a pie chart based on three different values, pause, nopause and sleeping. Initially it draws th pie chart correctly but after some redraw it gives me the following error.
Could not draw pie with labels contained inside canvas
My code is
Lecturer.socket.onmessage = function (message) {
var str = message.data;
var msg = str.split(":");
if(msg[0] == 'pause'){
var pause = parseInt(msg[1]);
var noPause = parseInt(msg[2]);
var sleeping = parseInt(msg[3]);
var data = [
{label: "Pause", data:pause},
{label: "No Pause", data:noPause},
{label: "Sleeping", data:sleeping}
];
var options = {
series: {
pie: {show: true}
},
legend: {
show: false
}
};
$.plot($("#pie-placeholder"), data, options);
}
};
HTML is
<div id="live-placeholder" class="flot"></div>
All the require js libraries are included. What I m doing wrong? Any Help ?
Thanks
You've got two problems:
1.) your placeholder div id doesn't match the $.plot call. live-placeholder != pie-placeholder.
2.) You don't need to calculate the percents yourself. Flot will do it internally.
See a working fiddle here.