Expose additional data based on xAxis element click - Highcharts.js - javascript

I'm facing some difficulties while trying to implement 'xAxis clickable' column chart. I'm trying to expose additional Pie charts below my column chart, based on user click on one of the element in xAxis.
The way the first graph is build:
function chartBuilder(data) {
if (data.length === 0) {
return null;
}
var categories = [];
var uniqData = [
{name : 'Fatal', data:[], color:'black', stack: 'fatal'},
{name : 'Critical', data:[], color:'red', stack: 'critical'},
];
_.each(data, function (item) {
categories = categories.concat(item.site);
var fatalValue = {};
fatalValue[item.site] = parseFloat(item.fatal || 0);
uniqData[0].data = uniqData[0].data.concat(fatalValue);
var criticalValue = {};
criticalValue[item.site] = parseFloat(item.critical || 0);
uniqData[1].data = uniqData[1].data.concat(criticalValue);
});
var chartConfig = util.basicConfigChart(categories, uniqData);
chartConfig.yAxis.title = {
text: 'Num Of Events'
};
chartConfig.xAxis.labels = {
formatter: function() {
var ret = this.value,
len = ret.length;
if (len > 10) {
ret = '<strong>' + ret.slice(0,ret.indexOf('_')) + '<br/>' + ret.slice(ret.indexOf('_') + 1, len) + '</strong>';
}
if (len > 25) {
ret = ret.slice(0,25) + '...';
}
return '<strong>' + ret + '</strong>';
},
useHTML: true
};
chartConfig.options.tooltip = {
formatter : function () {
return '<strong>' + this.series.name + '</strong>:' + this.point.y + '<br>Total: ' + this.point.total;
}
};
return chartConfig;
}
So basically, what I need is a way to determine which element in xAxis was clicked, and expose below pie charts with data relevant to this element.

If I understand you correctly, you want to add a click event on the xAxis columns. This can be done using a click event
events: {
click: function (event) {
alert(
'x index: ' + event.point.x + ', \n' +
'series.index: ' + event.point.series.index
);
}
}
This event can be added to a specific series, or to the plotOptions to affect all series.
The code above, will make an alert that shows the relevant indexes.
Working example: https://jsfiddle.net/ewolden/xr17pen6/6/
API on click event: https://api.highcharts.com/highcharts/series.column.events.click

Related

Reactjs Show Real-Time Data from Database in Popup Window

I have a GUI that shows live data of little icons moving around based on the data from my postgresql database. When I click on one of the icons, a popup window shows with the data from the database. However, it only shows data on the time the icon was clicked and I want the data displayed to update as the database updates. I could really use some help.
The Popup Window Event
addClickEvent(id, source, publish) {
this.map.on('click', id, function (e) {
let description = ''
let trackId = e.features[0].properties.id
let trackSource = e.features[0].properties.track_source
let trackIdWithSource = trackId + '_' + source
//HELPFUL TIP: This will need to be updated when you bring over an update to base with the previous version
for (const [key, value] of Object.entries(e.features[0].properties)) {
if (isLowerCase(key)) {
}
else { description += key + ': ' + value + '\n' }
}
let trackWindow = document.getElementById("a" + trackIdWithSource);
if (!trackWindow) {
let trackDescription = document.createElement('div')
trackDescription.id = "a" + trackIdWithSource
trackDescription.innerHTML = description;
trackDescription.style.whiteSpace = "pre-wrap"
document.getElementById('root').appendChild(trackDescription)
new WindowModal({ size: { x: 'max-content', y: 200 }, pos: { x: 300, y: 25 }, title: trackIdWithSource + " Info: ", elementSelector: '#a' + trackIdWithSource })
}
trackWindow = document.getElementById("a" + trackIdWithSource);
//let trackContent = trackWindow.getElementsByClassName("WindowModal-content")
if (publish) {
if (!document.getElementById('publish_' + trackIdWithSource)) {
let publishButton = document.createElement('button')
publishButton.innerHTML = "Publish"
publishButton.id = "publish_" + trackIdWithSource
publishButton.onclick = function () {
let url = process.env.REACT_APP_PROTOCOL + '://' + process.env.REACT_APP_PUBLISH_API_HOST + ':' + process.env.REACT_APP_PUBLISH_API_PORT + '/track/' + trackId + '&' + trackSource + '/publish'
fetch(url, { method: 'post' })
.then(function (data) {
return console.log(data.status);
})
}
publishButton.className = "button"
trackWindow.insertBefore(publishButton, trackWindow.children[1])
}
}
});
const popup = new maplibregl.Popup({
closeButton: false,
closeOnClick: false
});
this.map.on("mouseenter", id, function (e) {
const coordinates = e.features[0].geometry.coordinates.slice();
let description = e.features[0].properties.id;
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
popup.setLngLat(coordinates)
.setHTML(description)
.addTo(this);
});
this.map.on('mouseleave', id, () => {
this.map.getCanvas().style.cursor = '';
popup.remove();
});
}

Creating and appending google charts on the fly to the divs created dynamically in jquery

I have the following code, in which I iterate through a list of items and create dynamic div elements. Then, at the end of each loop, I want to create a barchart using google visualisations and append it to the divs created. However, I receive the following error.
Container is not defined
I am new to google visualisations. I wonder if this is possible. Below is my code:
function drawBarChart() {
PAGES = _.keys(_.countBy(perct_by_page_COLL_vs_NCOLL, function(data) { return data.PAGE_SHORT; }));
$.each(PAGES, function(index, item){
$("#BLOQUES").append("<div class='" + item + "'></div>")
page_sub = $.grep(perct_by_page_COLL_vs_NCOLL, function(n, i) {return n.PAGE_SHORT === item;});
container = $("#BLOQUES").find('.' + item)
container.append('<div>' + page_sub[0].PAGE_TITLE + '</div>')
container.append("<div class='barchart'></div>")
x = page_sub[0]
a = x['PAGE_SHORT']
b = x['PERC_ALL']
c = x['PERC_NCOLL']
d = x['PERC_ALL']
var data = google.visualization.arrayToDataTable([
['PAGE_SHORT', 'PERC_ALL', 'PERC_COLL', 'PERC_NCOLL'], [a, b, c, d]
]);
var options = {
title: '',
chartArea: {width: '50%'},
hAxis: {
title: 'Total Population',
minValue: 0
},
vAxis: {
title: 'City'
}
};
var chart = new google.visualization.BarChart(container.find('.barchart'));
chart.draw(data, options);
});
}
the chart is expecting a specific dom element
not a jquery reference, which is basically an array of elements
recommend using the loop index to assign an id to the chart element
container.append("<div id='chart" + index + "' class='barchart'></div>")
then using the id to create the chart
var chart = new google.visualization.BarChart($('#chart' + index)[0]);
full snippet...
function drawBarChart() {
PAGES = _.keys(_.countBy(perct_by_page_COLL_vs_NCOLL, function(data) { return data.PAGE_SHORT; }));
$.each(PAGES, function(index, item){
$("#BLOQUES").append("<div class='" + item + "'></div>")
page_sub = $.grep(perct_by_page_COLL_vs_NCOLL, function(n, i) {return n.PAGE_SHORT === item;});
container = $("#BLOQUES").find('.' + item)
container.append('<div>' + page_sub[0].PAGE_TITLE + '</div>')
container.append("<div id='chart" + index + "' class='barchart'></div>")
x = page_sub[0]
a = x['PAGE_SHORT']
b = x['PERC_ALL']
c = x['PERC_NCOLL']
d = x['PERC_ALL']
var data = google.visualization.arrayToDataTable([
['PAGE_SHORT', 'PERC_ALL', 'PERC_COLL', 'PERC_NCOLL'], [a, b, c, d]
]);
var options = {
title: '',
chartArea: {width: '50%'},
hAxis: {
title: 'Total Population',
minValue: 0
},
vAxis: {
title: 'City'
}
};
var chart = new google.visualization.BarChart($('#chart' + index)[0]);
chart.draw(data, options);
});
}

OpenLayers 4 - LayerFilter on forEachFeatureAtPixel

I have 2 vector layers of which I want only 1 to be selectable for a WFS get feature info layer. OL4 docs tell me there is an opt_layerfilter for the forEachFeatuerAtPixel function.
I’m in a similar situation like this: OpenLayers 3 hasFeatureAtPixel filter for layer.
Due to my lack of JavaScript knowledge I can’t seem to make it work with the following code in OpenLayers 4:
var displayFeatureInfo = function (pixel) {
var features = [];
map.forEachFeatureAtPixel(pixel, {
layerFilter: function (layer) {
return layer.get('name') === 'isochrones';
}
}, function (feature) {
features.push(feature);
});
if (features.length > 0) {
var info = [];
var i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
info.push('<div id="infobox">' + '<p2>' + 'Isochroon ' + features[i].get('name') + ', locatie ' + features[i].get('facilityid') + '</p2>' + '<p>' + 'aantal lopend: ' + features[i].get('n_pedestrians') + ', fiets: ' + features[i].get('n_bike') + ', ebike: ' + features[i].get('n_ebike') + '<br>' + 'speedpedelec: ' + features[i].get('n_speedpedelec') + ', auto: ' + features[i].get('n_car') + '</p>' + '</div>');
}
document.getElementById('info').innerHTML = info.join(', ') || '&nbsp';
} else {
document.getElementById('info').innerHTML = ' ';
}
};
map.on('click', function (evt) {
displayFeatureInfo(evt.pixel);
});
The layer I want to be selectable is named ‘isochrones’.
It throws me an error “d.call is not a function” when I try to click any vector layer in the map.
Could anyone point me in the right direction?
Looks like you have your args swapped.
The params for forEachFeatureAtPixel are (pixel, callback, options)
You have (pixel, options, callback)

Javascript If variable value type

I have the following function which populates elements options.
player.onTracksChanged_ = function(event) {
// Update the track lists.
var lists = {
video: document.getElementById('videoTracks'),
audio: document.getElementById('audiotrackButton'),
text: document.getElementById('captionButton')
};
var formatters = {
video: function(track) {
return track.width + 'x' + track.height + ', ' +
track.bandwidth + ' bits/s';
},
audio: function(track) {
return 'language: ' + track.language + ', ' +
track.bandwidth + ' bits/s';
},
text: function(track) {
return 'language: ' + track.language + ' ' +
'(' + track.kind + ')';
}
};
// Clear the old track lists.
Object.keys(lists).forEach(function(type) {
var list = lists[type];
while (list.firstChild) {
list.removeChild(list.firstChild);
}
});
// Populate with the new tracks.
var tracks = player.getTracks();
tracks.sort(function(t1, t2) {
// Sort by language, then by bandwidth.
if (t1.language) {
var ret = t1.language.localeCompare(t2.language);
if (ret) return ret;
}
return t1.bandwidth - t2.bandwidth;
});
tracks.forEach(function(track) {
var list = lists[track.type];
var option = document.createElement('option');
option.textContent = formatters[track.type](track);
option.track = track;
option.value = track.id;
option.selected = track.active;
list.appendChild(option);
});
};
What I am trying to achieve is an if statement based on this which determines if the number of each 'type' of 'track' is greater than or equal to 2, this is what I've got:-
var mediaTracks = player.getTracks();
if (mediaTracks.length >= 2) {
console.log('there are more than 2 tracks');
} else {
console.log('there are less than 2 tracks');
};
When in fact I need to do something more like this:-
var mediaTracksVideo = player.getTracks(video)
var mediaTracksAudio = player.getTracks(audio)
Then do something like:-
if (mediaTracksAudio >= 2) {...
But when I try player.getTracks(audio); the console logs 'audio is not defined'.
Any idea why?
With player.getTracks(audio) you're using the variable audio, which is undefined. You probably want to use player.getTracks('audio'), where 'audio' is a string.

How to change tooltip content in c3js

I'm working on a timeline display and I have data that I want to show on the tooltip. currently it only shows the value at each time. and I cannot find a way to change it. the example below shows how to change the value's format but not what values are displayed
var chart = c3.generate({
data: {
columns: [
['data1', 30000, 20000, 10000, 40000, 15000, 250000],
['data2', 100, 200, 100, 40, 150, 250]
],
axes: {
data2: 'y2'
}
},
axis : {
y : {
tick: {
format: d3.format("s")
}
},
y2: {
show: true,
tick: {
format: d3.format("$")
}
}
},
tooltip: {
format: {
title: function (d) { return 'Data ' + d; },
value: function (value, ratio, id) {
var format = id === 'data1' ? d3.format(',') : d3.format('$');
return format(value);
}
//value: d3.format(',') // apply this format to both y and y2
}
}
});
it's taken from http://c3js.org/samples/tooltip_format.html
they do admit that there isn't an example for content editing but I couldn't find anything in the reference or forums, but a suggestion to change the code (it's here: https://github.com/masayuki0812/c3/blob/master/c3.js in line 300) and below:
__tooltip_contents = getConfig(['tooltip', 'contents'], function (d, defaultTitleFormat, defaultValueFormat, color) {
var titleFormat = __tooltip_format_title ? __tooltip_format_title : defaultTitleFormat,
nameFormat = __tooltip_format_name ? __tooltip_format_name : function (name) { return name; },
valueFormat = __tooltip_format_value ? __tooltip_format_value : defaultValueFormat,
text, i, title, value, name, bgcolor;
for (i = 0; i < d.length; i++) {
if (! (d[i] && (d[i].value || d[i].value === 0))) { continue; }
if (! text) {
title = titleFormat ? titleFormat(d[i].x) : d[i].x;
text = "<table class='" + CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : "");
}
name = nameFormat(d[i].name);
value = valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index);
bgcolor = levelColor ? levelColor(d[i].value) : color(d[i].id);
text += "<tr class='" + CLASS.tooltipName + "-" + d[i].id + "'>";
text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>" + name + "</td>";
text += "<td class='value'>" + value + "</td>";
text += "</tr>";
}
return text + "</table>";
})
did anyone attempted to do so? developed some function to facilitate the process? have any tips on how to do so correctly? I do not know how to change their code in a way I could use more data or data different than the d value the function gets.
If you use the function getTooltipContent from https://github.com/masayuki0812/c3/blob/master/src/tooltip.js#L27 and add it in the chart declaration, in tooltip.contents, you'll have the same tooltip content that the default one.
You can make changes on this code and customize it as you like. One detail, as CLASS is not defined in the current scope, but it's part chart object, I substituted CLASS for $$.CLASS, maybe you don't even need this Object in your code.
var chart = c3.generate({
/*...*/
tooltip: {
format: {
/*...*/
},
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
var $$ = this, config = $$.config,
titleFormat = config.tooltip_format_title || defaultTitleFormat,
nameFormat = config.tooltip_format_name || function (name) { return name; },
valueFormat = config.tooltip_format_value || defaultValueFormat,
text, i, title, value, name, bgcolor;
for (i = 0; i < d.length; i++) {
if (! (d[i] && (d[i].value || d[i].value === 0))) { continue; }
if (! text) {
title = titleFormat ? titleFormat(d[i].x) : d[i].x;
text = "<table class='" + $$.CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : "");
}
name = nameFormat(d[i].name);
value = valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index);
bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id);
text += "<tr class='" + $$.CLASS.tooltipName + "-" + d[i].id + "'>";
text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>" + name + "</td>";
text += "<td class='value'>" + value + "</td>";
text += "</tr>";
}
return text + "</table>";
}
}
});
If you want to control tooltip render and use default rendering depending on data value you can use something like this:
tooltip: {
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
if (d[1].value > 0) {
// Use default rendering
return this.getTooltipContent(d, defaultTitleFormat, defaultValueFormat, color);
} else {
return '<div>Show what you want</div>';
}
},
format: {
/**/
}
}
In my case i had to add the day for the date value(x axis) in tool tip. Finally i came came up with the below solution
References for js and css
https://code.jquery.com/jquery-3.2.1.js
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js
https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.10/c3.min.js
https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.10/c3.min.css
function toDate(dateStr)
{
var numbers = dateStr.match(/\d+/g);
return new Date(numbers[0], numbers[1]-1, numbers[2]);
}
function GetMonthFromString(month)
{
var months = {'Jan' : '01','Feb' : '02','Mar':'03','Apr':'04',
'May':'05','Jun':'06','Jul':'07','Aug':'08','Sep':'09',
'Oct':'10','Nov':'11','Dec':'12'};
return months[month];
}
function GetFullDayName(formatteddate)
{
var weekday = new Array(7);
weekday[0] = "Sunday";
weekday[1] = "Monday";
weekday[2] = "Tuesday";
weekday[3] = "Wednesday";
weekday[4] = "Thursday";
weekday[5] = "Friday";
weekday[6] = "Saturday";
var dayofdate = weekday[formatteddate.getDay()];
return dayofdate;
}
//Chart Data for x-axis, OnHours and AvgHours
function CollectChartData()
{
var xData = new Array();
var onHoursData = new Array();
var averageHoursData = new Array();
var instanceOccuringDatesArray = ["2017-04-20","2017-04-21","2017-04-22","2017-04-23","2017-04-24","2017-04-25","2017-04-26","2017-04-27","2017-04-28","2017-04-29","2017-04-30","2017-05-01","2017-05-02","2017-05-03","2017-05-04","2017-05-05","2017-05-06","2017-05-07","2017-05-08","2017-05-09","2017-05-10","2017-05-11","2017-05-12","2017-05-13","2017-05-14","2017-05-15","2017-05-16","2017-05-17","2017-05-18","2017-05-19","2017-05-20"];
var engineOnHoursArray = ["4.01","14.38","0.10","0.12","0.01","0.24","0.03","6.56","0.15","0.00","1.15","0.00","1.21","2.06","8.55","1.41","0.03","1.42","0.00","3.35","0.02","3.44","0.05","5.41","4.06","0.02","0.04","7.26","1.02","5.09","0.00"];
var avgUtilizationArray = ["2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29","2.29"];
xData.push('x');
onHoursData.push('OnHours');
averageHoursData.push('Project Average');
for(var index=0;index<instanceOccuringDatesArray.length;index++)
{
xData.push(instanceOccuringDatesArray[index]);
}
for(var index=0;index<engineOnHoursArray.length;index++)
{
onHoursData.push(engineOnHoursArray[index]);
}
for(var index=0;index<avgUtilizationArray.length;index++)
{
averageHoursData.push(avgUtilizationArray[index]);
}
var Data = [xData, onHoursData, averageHoursData];
return Data;
}
function tooltip_contents(d, defaultTitleFormat, defaultValueFormat, color) {
var $$ = this, config = $$.config, CLASS = $$.CLASS,
titleFormat = config.tooltip_format_title || defaultTitleFormat,
nameFormat = config.tooltip_format_name || function (name) { return name; },
valueFormat = config.tooltip_format_value || defaultValueFormat,
text, i, title, value, name, bgcolor;
// You can access all of data like this:
//$$.data.targets;
for (i = 0; i < d.length; i++) {
if (! text) {
title = titleFormat ? titleFormat(d[i].x) : d[i].x;
var arr = title.split(" ");
var datestr = new Date().getFullYear().toString() + "-"+ GetMonthFromString(arr[1]) + "-"+ arr[0];
var formatteddate = toDate(datestr);
var dayname = GetFullDayName(formatteddate);
title = title + " (" + dayname + ")";
text = "<table class='" + $$.CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : "");
}
name = nameFormat(d[i].name);
var initialvalue = valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index);
if (initialvalue.toString().indexOf('.') > -1)
{
var arrval = initialvalue.toString().split(".");
value = arrval[0] + "h " + arrval[1] + "m";
}
else
{
value = initialvalue + "h " + "00m";
}
bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id);
text += "<tr class='" + CLASS.tooltipName + "-" + d[i].id + "'>";
text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>" + name + "</td>";
text += "<td class='value'>" + value + "</td>";
text += "</tr>";
}
return text + "</table>";
}
$(document).ready(function () {
var Data = CollectChartData();
var chart = c3.generate({
data: {
x: 'x',
columns: Data
},
axis: {
x: {
type: 'timeseries',
tick: {
rotate: 75,
//format: '%d-%m-%Y'
format: '%d %b'
}
},
y : {
tick : {
format: function (y) {
if (y < 0) {
}
return y;
}
},
min : 0,
padding : {
bottom : 0
}
}
},
tooltip: {
contents: tooltip_contents
}
});
});
<script src="https://code.jquery.com/jquery-3.2.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.10/c3.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.10/c3.min.css" rel="stylesheet" />
<div id="chart"></div>
When we have a stacked bar chart and we would like to show "Total" in the tooltip (but not in the chart as a bar/stack) this can come handy.
C3 charts use a array to store the data for tooltips and before the tooltips are displayed we are adding totals (or anyother data as per our requirement). By doing this though the totals is not available as a stack it is shown in the tooltip.
function key_for_sum(arr) {
return arr.value; //value is the key
};
function sum(prev, next) {
return prev + next;
}
var totals_object = {};
totals_object.x = d[0]['x'];
totals_object.value = d.map(key_for_sum).reduce(sum);
totals_object.name = 'total';
totals_object.index = d[0]['index'];
totals_object.id = 'total';
d.push(totals_object);
Above code has been added to ensure that total is available in
C3.js Stacked Bar chart's tooltip
var chart = c3.generate({
/*...*/
tooltip: {
format: {
/*...*/
},
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
function key_for_sum(arr) {
return arr.value; //value is the key
}
function sum(prev, next) {
return prev + next;
}
var totals_object = {};
totals_object.x = d[0]['x'];
totals_object.value = d.map(key_for_sum).reduce(sum);// sum func
totals_object.name = 'total';//total will be shown in tooltip
totals_object.index = d[0]['index'];
totals_object.id = 'total';//c3 will use this
d.push(totals_object);
var $$ = this,
config = $$.config,
titleFormat = config.tooltip_format_title || defaultTitleFormat,
nameFormat = config.tooltip_format_name || function (name) {
return name;
},
valueFormat = config.tooltip_format_value || defaultValueFormat,
text, i, title, value, name, bgcolor;
for (i = 0; i < d.length; i++) {
if (!(d[i] && (d[i].value || d[i].value === 0))) {
continue;
}
if (!text) {
title = titleFormat ? titleFormat(d[i].x) : d[i].x;
text = "<table class='" + $$.CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : "");
}
name = nameFormat(d[i].name);
value = valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index);
bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id);
text += "<tr class='" + $$.CLASS.tooltipName + "-" + d[i].id + "'>";
text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>" + name + "</td>";
text += "<td class='value'>" + value + "</td>";
text += "</tr>";
}
return text + "</table>";
}
}
Adding additional content or non-numerical data into the chart tooltips can be done.
This builds on #supita's excellent answer http://stackoverflow.com/a/25750639/1003746.
Its possible to insert additional metadata about each line into the classes parameter when generating/updating the chart. These can then be added as rows to the tooltip.
This doesn't seem to affect the chart - unless you are using the data.classes feature.
data: {
classes: {
data1: [{prop1: 10, prop2: 20}, {prop1: 30, prop2: 40}],
data2: [{prop1: 50, prop2: 60}'{prop1: 70, prop2: 80}]
}
}
To pick up the metadata in the config.
tooltip: {
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
const $$ = this;
const config = $$.config;
const meta = config.data_classes;
...
for (i = 0; i < d.length; i++) {
if (! (d[i] && (d[i].value || d[i].value === 0))) { continue; }
if (! text) {
...
}
const line = d[0].id;
const properties = meta.classes[line];
const property = properties? properties[i] : null;
Then add the following rows to the table to show the new properties.
if (property ) {
text += "<tr class='" + $$.CLASS.tooltipName + "-" + d[i].id + "'>";
text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>PROP1</td>";
text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>" + property.prop1 + "</td>";
text += "</tr>";
text += "<tr class='" + $$.CLASS.tooltipName + "-" + d[i].id + "'>";
text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>PROP2</td>";
text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>" +
property.prop2+ " cm/s</td>";
If anybody cares, here is a ClojureScript version of the above algorithm (e.g. supita's answer), slightly simplified (without support for config). (This is probably nothing the OP asked for, but as of now there are so few resources on the net on this topic that most people might wind up here.)
:tooltip {
:contents
(fn [d default-title-format default-value-format color]
(this-as this
(let [this-CLASS (js->clj (.-CLASS this) :keywordize-keys true)
tooltip-name-class (:tooltipName this-CLASS)
rows (js->clj d :keywordize-keys true)
title-row (->> (first rows) (#(str "<table class='" (:tooltip this-CLASS)
"'><tr><th colspan='2'>"
(default-title-format (:x %)) "</th></tr>")))
data-rows (->> rows
(map #(str "<tr class='" tooltip-name-class "--" (:id %) "'>"
"<td class='name'><span style='background-color:"
(color (:id %)) "'></span>" (:name %) "</td>"
"<td class='value'>" (default-value-format (:value %)) "</td>"
"</tr>")))]
(str title-row (string/join data-rows) "</table>"))))}
Your question is about changing the content of the tooltip in c3js.
The tooltip has 3 variables
+----------------+
| title |
+----------------+
| name | value |
+----------------+
Plus, you want to add 'name' from an additional variable, other than those used in 'column'.
tooltip: {
format: {
title(x, index) { return ''; },
name(name, ratio, id, index) { return lst[index + 1]; },
value(value, ratio, id, index) { return value; }
}
},
this worked for me, feel free to play around with the arguments, to get what you need.
I faced a problem which is related tooltip position and style for c3 before. in order to arrange tooltip in c3 freely, my suggestion is manipulating tooltip with d3.
// internal = chart.internal()
const mousePos = d3.mouse(internal.svg.node()); // find mouse position
const clientX = mousePos[0]; //for x
const clientY = mousePos[1]; //for y
const tooltip = d3.select("#tooltip"); //select tooltip div (apply your style)
tooltip.style("display", "initial"); //show tooltip
tooltip.style("left", clientX - mouseOffSet.X + "px"); // set position
tooltip.style("top", clientY - mouseOffSet.Y + "px"); // set position
tooltip.html("<span>" + content + "</span>");
// you can arrange all content and style whatever you want
<div
id="tooltip"
className="your-style"
style={{ display: "none", position: "absolute" }}
/>
Good luck!!

Categories

Resources