I have inserted a highchart graph in qualtrics for a question I wanted to ask. Respondents answer the question by dragging the line.
I wanted to add a button to reset the values of the line to the initial values I showed them as default, or to show them specific values.
Here are the html elements of the question
<div id="container" style="height: 400px"> </div>
<button id="button">Set new data</button>
Whereas here is the js
Qualtrics.SurveyEngine.addOnload(function()
{
var chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
animation: false
},
legend: {
enabled: false
},
xAxis: {
categories: ['1', '2', '3']
},
yAxis: {
min: -2,
max: 10,
tickInterval: 2,
lineWidth: 1,
title: {
text: '\% points'
},
},
plotOptions: {
series: {
point: {
events: {
drag: function (e) {
// Returning false stops the drag and drops. Example:
/*
if (e.newY > 300) {
this.y = 300;
return false;
}
*/
j('#drag').html(''
'Dragging <b>' + this.category + '</b> to <b>' + Highcharts.numberFormat(e.y,2) + '\% </b>');
},
drop: function () {
j('#drop').html(''
this.category + '</b> was set to <b>' + Highcharts.numberFormat((Math.ceil(this.y*20)/20),2) + '\% </b>');
}
}
},
tooltip: {
valueDecimals:2
},
stickyTracking: true,
getExtremesFromAll: true,
marker: {
radius: 6
}
},
/* line: {
cursor: 'ns-resize'
}*/
},
tooltip: {
yDecimals: 2
},
series: [{
data: [0, 0, 0],
dragMinY:-2,
dragMaxY:10,
draggableY: true,
cursor: 'ns-resize'
}],
});
j('#button1').click(function () {
chart.series[0].setData([5, 6, 2] );
});
j('#button2').click(function () {
chart.series[0].setData([0, 0, 0]);
});
});
(Notice that, since Jquery(preloaded in the header) conflicts with prototype I reset the $ to j to prevent conflicts).
Now, the buttons should work, they should do what they are meant to, but something is odd: if I click on the graph to drag the line, the first button is automatically clicked, and I do not understand why. If I click the second both are clicked. What's worst, every time I click the page reloads. This is specific to Qualtrics only, I tried to implement this in jfiddle (https://jsfiddle.net/tg35u440/) and everything seemed to be fine.
So, do you know what I actually have to do to prevent qualtrics from reloading the oage everytime a button is clicked on? And why are the buttons creating a conflict with the dragging function on screen in Qualtrics?
Thank you very much!
Related
Hi I am trying to present the columns values at the top of each column so it is easier for the user to compare values.
I have got this working but when some columns are very close to each other some of the values aren't being dispalyed because I'm presuming highcharts works out that it wqouldnt fit unless it overlapped so it doesnt show the value.
Here is the code for my chart -
var chart_1_Options = {
chart: {
renderTo: 'container_chart_1',
backgroundColor: '#FFFFFF'
},
credits: {
enabled: false
},
title: {
text: ''
},
xAxis: {
type: 'datetime'
},
yAxis: {
title: {
text: null
}
},
plotOptions: {
column: {
animation: false,
borderWidth: 0,
dataLabels: {
enabled: true,
formatter: function() {
return '<p style="font-size: 8px;">' + this.y + '% </p>';
}
}
}
},
legend: {
enabled: true
}
};
Here is an image of what the chart looks like, I have circled areas where the value is missing
I just want the value to show even if it can't fit, I want it to place the value somewhere inside the column if it cant fit at the top.
Thanks for any help!
I think that you can try to find those labels and show them by doing something like code below:
events: {
load() {
let chart = this;
chart.series[0].points.forEach(p => {
setTimeout(function() {
if (p.dataLabel.translateY === -9999) {
p.dataLabel.translate(p.dataLabel.alignAttr.x, p.dataLabel.alignAttr.y + 20)
p.dataLabel.css({
opacity: 1
})
}
}, 50)
})
}
}
Demo: https://jsfiddle.net/BlackLabel/unzgsmfr/
API: https://api.highcharts.com/highcharts/chart.events.load
I am a bit out of my comfort zone, since I normally do analytics and not fancy front-ends. However, I would like to have a real-time demo of some of my work, so it becomes easier to understand and not just numbers in a matrix. I have looked around and found something semi-relevant and come this far:
(It has four series like I want to and it iterates - to some degree)
https://jsfiddle.net/023sre9r/
var series1 = this.series[0],
series2 = this.series[1],
series3 = this.series[2],
series4 = this.series[3];
But I am totally lost on how to remove the random number generators without loosing nice things like the number of data points in a view (seems to depend on the for loop?!). Remove the extra title "Values" right next to my real y-axis title. And of cause how to get a new data point from a XML-file every second.
Ideally I want to have an XML-file containing 4 values, which I update approximately every 200ms in MATLAB. And every second I would like my 4 series chart to update. Is it not relatively easy, if you know what you are doing?! :-)
Thanks in advance!
I simplified your example and added clear code showing how to fetch data from server and append it to your chart using series.addPoint method. Also if you want to use XML, just convert it to JS object / JSON.
const randomData = () => [...Array(12)]
.map((u, i) => [new Date().getTime() + i * 1000, Math.random()])
Highcharts.chart('container', {
chart: {
renderTo: 'container',
type: 'spline',
backgroundColor: null,
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load () {
const chart = this
setInterval(() => {
// Fetch example below (working example: https://github.com/stpoa/live-btc-chart/blob/master/app.js)
// window.fetch('https://api.cryptonator.com/api/ticker/btc-usd').then((response) => {
// return response.json()
// }).then((data) => {
// chart.series[0].addPoint({ x: data.timestamp * 1000, y: Number(data.ticker.price) })
// })
chart.series.forEach((series) => series.addPoint([new Date().getTime(), Math.random()], true, true))
}, 3000)
}
}
},
title: {
text: null
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: [{
title: {
text: 'Temperature [°C]',
margin: 30
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
}, {
}],
tooltip: {
formatter: function() {
return '<b>' + this.series.name + '</b><br/>' +
Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' + Highcharts.numberFormat(this.y, 4);
}
},
legend: {
enabled: true
},
exporting: {
enabled: false
},
rangeSelector: {
enabled: false
},
navigator: {
enabled: false
},
scrollbar: {
enabled: false
},
series: [{
name: 'Setpoint',
data: randomData()
}, {
name: 'Return',
data: randomData()
}, {
name: 'Supply',
data: randomData()
}, {
name: 'Output',
data: randomData()
}]
})
Live example: https://jsfiddle.net/9gw4ttnt/
Working one with external data source: https://jsfiddle.net/111u7nxs/
Note: I have already read this question: Highcharts - Dyanmic graph with no initial data
And the effect I want to achieve is similar to this: http://jsfiddle.net/7vZ5a/40/. However, instead of updating every second, I would like it to update per click of a button. This is what I get so far:
http://jsfiddle.net/7vZ5a/49/
$(function() {
var chartData = [50, 60, 70, 100, 120, 200];
var timeStamps = [];
var index = 1;
$('#b').click(function() {
timeStamps.push(new Date());
var buttonB = document.getElementById('b');
buttonB.disabled = true;
if (index < chartData.length) {
setTimeout(function() {
if (index == 1) {
$('#container').highcharts().series[0].addPoint([0, chartData[0]], true, false);
$('#container').highcharts().series[0].addPoint([index, chartData[index]], true, false);
} else {
$('#container').highcharts().series[0].addPoint([index, chartData[index]], true, true);
index++;
}
}, 1000);
}
if (index < chartData.length - 1) {
setTimeout(function() {
buttonB.disabled = false;
}, 1500);
} else {
setTimeout(function() {
buttonB.style.visibility = "hidden";
}, 1500);
}
if (index == chartData.length - 2) {
setTimeout(function() {
document.getElementById('b').innerHTML = 'Letzte Ziehung';
}, 1000);
}
console.log(timeStamps);
})
Highcharts.setOptions({
lang: {
decimalPoint: ','
},
});
$('#container').highcharts({
chart: {
type: 'line',
marginBottom: 60
},
colors: [
'#0000ff',
],
title: {
text: ''
},
xAxis: {
title: {
text: 'Time',
offset: 23
},
min: 0,
max: 1,
tickInterval: 1,
gridLineWidth: 1
},
yAxis: {
title: {
text: 'Value'
},
min: 0,
max: 200
},
plotOptions: {
line: {
marker: {
enabled: false
}
}
},
tooltip: {
formatter: function() {
return Highcharts.numberFormat(this.y, 2) + 'GE';
}
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
credits: {
enabled: false
},
series: [{
name: '',
data: []
}]
});
});
Why (almost) the same code could work on the case when the chart update automatically but not on the case when the update is triggered by click?
(I also tried the function with the code controlling update not in "setTimeout" block, it also did not work.)
What you want to do is create a function that adds data to a series, like:
function addData(series, data) {
series.addPoint([series.length, data], true, false);
}
This for example adds a new point with value data to the end of series (indexed series.length, which is one index higher than the last index of the series).
Then you can bind this function to the click event of your button:
$('#add-random-data').on('click', function () {
addData(chart.series[0], Math.random());
});
This adds a random value between 0 and 1 to the series. It is assumed that the chart is stored in a variable named chart, and series[0] is the series of that chart that you want to add the data to.
This fiddle shows this on the given example chart.
The reason why your code doesn't work is that you set min and max of your x-axis, as pointed out in the comments. That way the chart won't plot data that is indexed outside of your limits. You can just let the chart auto-scale its axes by removing the min and max properties. If you want the values to always be between 0 and 1 on the x-axis, you have to overwrite the points with index 0 and 1 in your series.
As you can see from this basic example, http://www.highcharts.com/demo/pie-basic
when you select a pie, the selected pie got pulled out slowly with animation.
and that is handled by this option.
pie: {
allowPointSelect: true
}
However, I don't want mouse click to select Point. I have, say, a button outside of the pie chart, when I press the button, the first pie/Point should be selected.
I have the following
$scope.SAWChartConfig.series[0].data[0].selected = true;
$scope.SAWChartConfig.series[0].data[0].sliced = true;
programatically set the first point as selected when button clicked. It works fine, but it lost the animation (where it should slowly pull outward).
My questions are:
how can i add the animation back?
series[0].data contains a few data points, I would need to reset data[i].sliced to false for each of them after button is clicked. Is there a easy way to do it instead of loop through all items?
.controller('spendingPieChartCtrl', ['$scope', '$q',
'TransactionService', 'CategoryService','chartColors', function
($scope, $q, TransactionSvc, CategorySvc, chartColors) {
if(typeof $scope.height === 'undefined') {
$scope.height = 140;
}
$scope.SAWChartConfig = {
options: {
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
type: 'pie',
margin: [0, 0, 0, 0],
spacingTop: 0,
spacingBottom: 0,
spacingLeft: 0,
spacingRight: 0,
height: $scope.height
},
plotOptions: {
pie: {
dataLabels: {
enabled: false
},
size: '100%',
borderWidth: 0
},
series: {
states: {
hover: {
enabled: false
}
}
}
},
title:{
text: null
},
tooltip: {
enabled: false
}
},
series: [{
data: []
}]
};
$q.all([
TransactionSvc.get(),
CategorySvc.get()
]).then(function() {
var spendingCategories = TransactionSvc.getTopCategories();
//redraw the chart by updating series data
$scope.SAWChartConfig.series = [{data: spendingCategories}];
});
$scope.$on('talkToOne', function( event, data ){
$scope.SAWChartConfig.series[0].data[index].select();
//$scope.SAWChartConfig.series[0].data[index].sliced = true;
});
I am using ng-hightcharts, and here is the directive call
chart.series[0].data[index].select() will do the select/deselect of the slices without loosing animation.
See the working example here
By using above code your second problem will also get fixed, since the next select call will automatically deselect other selected slices in the chart.
I have a div (overflow: auto) to which I dynamically add inner divs after a certain period. When a new one is added, it is added to the beginning. Each one of the inner divs have a jqPlot chart, and as long as there is just one it works fine, but as soon as another div is added two things happen with the old one(s):
The chart is moved further down in the div.
The chart has no plots or background (although it has axes marks).
According to the developer tools, all canvases are positioned correctly, but they are empty. This is the code used to add new charts (chart_div_? exists):
$.jqplot('chart_div_' + chartCounter, sold_plot, {
seriesColors: [ "#30D2FF", "BFFFCB", "BFFFCB", "BFFFCB" ],
seriesDefaults: {
showMarker: false,
markerOptions: {
show: false,
}
},
axes: {
xaxis: {
renderer: $.jqplot.DateAxisRenderer,
min: plot_min,
max: plot_max,
}
},
grid: {
background: '#444444',
},
});
chartCounter++;
Could it be something to do with moving a canvas? I tried redrawing it, but it did not work.
here is the example which can help you: Jsfiddle Link
HTML:
<div id="main">
<div id="chart1" style="margin-top:20px; margin-left:20px;"></div>
</div>
Click Here Trigger
Javascript:
$(document).ready(function () {
$.jqplot.config.enablePlugins = true;
var chartData = [
["19-Jan-2012", 2.61],
["20-Jan-2012", 5.00],
["21-Jan-2012", 6.00]
];
var cnt = 1;
// add a custom tick formatter, so that you don't have to include the entire date renderer library.
$.jqplot.DateTickFormatter = function (format, val) {
// for some reason, format isn't being passed through properly, so just going to hard code for purpose of this jsfiddle
val = (new Date(val)).getTime();
format = '%b %#d'
return $.jsDate.strftime(val, format);
};
function PlotChart(chartData, extraDays, elem) {
var plot2 = $.jqplot(elem, [chartData], {
title: 'Mouse Cursor Tracking',
seriesDefaults: {
renderer: $.jqplot.BarRenderer,
rendererOptions: {
barPadding: 1,
barWidth: 50
},
pointLabels: {
show: true
}
},
axes: {
xaxis: {
pad: 1,
// a factor multiplied by the data range on the axis to give the
renderer: $.jqplot.CategoryAxisRenderer,
// renderer to use to draw the axis,
tickOptions: {
formatString: '%b %#d',
formatter: $.jqplot.DateTickFormatter
}
},
yaxis: {
tickOptions: {
formatString: '$%.2f'
}
}
},
highlighter: {
sizeAdjust: 7.5
},
cursor: {
show: true
}
});
}
PlotChart(chartData, 3, "chart1");
$("a.topopup").click(function () {
loading();
return false;
});
function loading() {
var div = $("#main");
cnt = cnt + 1;
var elemId = "chart" + cnt;
div.prepend("<div id='" + elemId + "'></div>");
PlotChart(chartData, 3, elemId);
}
});