Reputation: 850
My goal is to create custom tooltips and also to always show them. There will be some more fancy stuff to it later on, but right now I am trying to achieve those two things.
For creating custom tooltips I use the tutorial of the charts.js docs; here on stackoverflow I found a solution for always showing the tooltips.
So right now I have two tooltips: standard tooltips, that appear afterDraw and stay visible, and custom tooltips, that are working so far but only appear on hovering dots/datapoints.
jsFiddle https://jsfiddle.net/2c4261wj/1/ and following image show the problem. The red container shows the custom tooltip, only appearing on hovering a dot.
And instead of the standard tooltips, I want to always show the custom ones afterDraw, but I can't manage to do that.
raw codebits
Custom tooltip in the tooltip options:
custom: function(tooltipModel) {
// Tooltip Element
var tooltipEl = document.getElementById('chartjs-tooltip');
// Create element on first render
if (!tooltipEl) {
tooltipEl = document.createElement('div');
tooltipEl.id = 'chartjs-tooltip';
tooltipEl.innerHTML = "<table></table>";
document.body.appendChild(tooltipEl);
}
// Hide if no tooltip
if (tooltipModel.opacity === 0) {
tooltipEl.style.opacity = 1;
return;
}
// Set caret Position
tooltipEl.classList.remove('above', 'below', 'no-transform');
if (tooltipModel.yAlign) {
tooltipEl.classList.add(tooltipModel.yAlign);
} else {
tooltipEl.classList.add('no-transform');
}
function getBody(bodyItem) {
return bodyItem.lines;
}
// Set Text
if (tooltipModel.body) {
var titleLines = tooltipModel.title || [];
var bodyLines = tooltipModel.body.map(getBody);
var innerHtml = '<div>';
titleLines.forEach(function(title) {
innerHtml += '<span>' + title + '</span>';
});
innerHtml += '</div>';
bodyLines.forEach(function(body, i) {
var colors = tooltipModel.labelColors[i];
var style = 'background:' + colors.backgroundColor;
style += '; border-color:' + colors.borderColor;
style += '; border-width: 2px';
var span = '<span class="chartjs-tooltip-key" style="' + style + '"></span>';
innerHtml += '<div class="inner">' + span + body + '</div>';
});
innerHtml += 'last';
var tableRoot = tooltipEl;
tableRoot.innerHTML = innerHtml;
}
// `this` will be the overall tooltip
var position = this._chart.canvas.getBoundingClientRect();
// Display, position, and set styles for font
tooltipEl.style.opacity = 1;
tooltipEl.style.left = position.left + tooltipModel.caretX - 100 + 'px';
tooltipEl.style.top = position.top + tooltipModel.caretY - 100 + 'px';
tooltipEl.style.fontFamily = tooltipModel._fontFamily;
tooltipEl.style.fontSize = tooltipModel.fontSize;
tooltipEl.style.fontStyle = tooltipModel._fontStyle;
tooltipEl.style.padding = tooltipModel.yPadding + 'px ' + tooltipModel.xPadding + 'px';
}
Always show tooltips:
Chart.pluginService.register({
beforeRender: function (chart) {
if (chart.config.options.showAllTooltips) {
// create an array of tooltips
// we can't use the chart tooltip because there is only one tooltip per chart
chart.pluginTooltips = [];
chart.config.data.datasets.forEach(function (dataset, i) {
chart.getDatasetMeta(i).data.forEach(function (sector, j) {
chart.pluginTooltips.push(new Chart.Tooltip({
_chart: chart.chart,
_chartInstance: chart,
_data: chart.data,
_options: chart.options.tooltips,
_active: [sector]
}, chart));
});
});
// turn off normal tooltips
chart.options.tooltips.enabled = false;
}
},
afterDraw: function (chart, easing) {
if (chart.config.options.showAllTooltips) {
// we don't want the permanent tooltips to animate, so don't do anything till the animation runs atleast once
if (!chart.allTooltipsOnce) {
if (easing !== 1)
return;
chart.allTooltipsOnce = true;
}
// turn on tooltips
chart.options.tooltips.enabled = true;
Chart.helpers.each(chart.pluginTooltips, function (tooltip) {
tooltip.initialize();
tooltip.update();
// we don't actually need this since we are not animating tooltips
tooltip.pivot();
tooltip.transition(easing).draw();
});
chart.options.tooltips.enabled = false;
}
}
});
I tried to use the custom tooltip right inside the first plugin but with not results, maybe I referenced it wrong in the plugin. Any ideas?
Upvotes: 0
Views: 3267
Reputation: 850
So, I found a solution. Before, I tried to use the custom tooltip and had a plugin created, which should make the custom tooltips appear on load and stay visible.
What I now did was:
First: working fiddle of the current state
Actually it is pretty simple; the plugin gets all the datasets, loops through them and draws the stuff on the canvas. Thanks to the plugin you can access all the data by saving it to arrays and then yea... grab them and do whatever you want.
The magic is happening here:
Chart.plugins.register({
afterDraw: function(chart, easing) {
// To only draw at the end of animation, check for easing === 1
var debug = false;
var ctx = chart.chart.ctx;
var numbers = [],
position = [],
dataString = [],
safetyCounter = 0;
var datasetAmount = chart.data.datasets.length;
var datasetLength = chart.getDatasetMeta(0).data.length;
var amountLength = (datasetAmount * datasetLength);
chart.data.datasets.forEach(function(dataset, i) {
var meta = chart.getDatasetMeta(i);
if (!meta.hidden) {
meta.data.forEach(function(element, index) {
if (numbers.indexOf(dataset.data[index]) < 0) {
numbers.push(dataset.data[index]);
}
position.push(element.tooltipPosition());
var fontSize = 20;
var fontStyle = 'bold';
var fontFamily = 'Helvetica Neue';
ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily);
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.lineWidth = 2;
dataString.push(dataset.data[index].toString() + '%');
var padding = 5;
if (numbers.length === amountLength) {
if (safetyCounter === 0) {
safetyCounter++
}
for (var k = 0; k < numbers.length; k++) {
if (k < datasetLength) {
ctx.strokeStyle = "#c7d85e";
ctx.fillStyle = "#c7d85e";
if (numbers[k] >= numbers[k + datasetLength]) {
ctx.beginPath();
ctx.moveTo(position[k].x, position[k].y - 50);
ctx.lineTo(position[k].x, position[k].y);
ctx.stroke();
ctx.fillText(dataString[k], position[k].x, position[k].y - 50 - (fontSize / 2) - padding);
} else {
ctx.beginPath();
ctx.moveTo(position[k].x, position[k].y + 50);
ctx.lineTo(position[k].x, position[k].y);
ctx.stroke();
ctx.fillText(dataString[k], position[k].x, position[k].y + 80 - (fontSize / 2) - padding);
}
} else {
ctx.strokeStyle = "#4f708b";
ctx.fillStyle = "#4f708b";
if (numbers[k] >= numbers[k - datasetLength]) {
ctx.beginPath();
ctx.moveTo(position[k].x, position[k].y - 50);
ctx.lineTo(position[k].x, position[k].y);
ctx.stroke();
ctx.fillText(dataString[k], position[k].x, position[k].y - 50 - (fontSize / 2) - padding);
} else {
ctx.beginPath();
ctx.moveTo(position[k].x, position[k].y + 50);
ctx.lineTo(position[k].x, position[k].y);
ctx.stroke();
ctx.fillText(dataString[k], position[k].x, position[k].y + 80 - (fontSize / 2) - padding);
}
}
}
} else {
// nothing yet here
}
});
}
});
si++;
// necessary for prevent standard tooltips from showing
// but I also use css pointer events for preventing canvas to respond to mouseevents
if (chart.config.options.showAllTooltips) {
if (!chart.allTooltipsOnce) {
if (easing !== 1) return;
chart.allTooltipsOnce = true;
}
chart.options.tooltips.enabled = true;
Chart.helpers.each(chart.pluginTooltips, function(tooltip) {
tooltip.initialize();
tooltip.update();
tooltip.pivot();
tooltip.transition(easing).draw();
});
chart.options.tooltips.enabled = false;
}
}
});
Upvotes: 1