ChartJS – Display a single line data in tooltip

0πŸ‘

βœ…

If this is the only chart you have (i.e. because the following code changes some of the global chart.js elements), you can use the following bit of code

var originalMultiTooltip = Chart.MultiTooltip;
Chart.MultiTooltip = function () {
    var argument = arguments[0];
    // locate the series using the active point
    var activeDatasetLabel = myChart.activeElements[0].datasetLabel;
    myChart.datasets.forEach(function (dataset) {
        if (dataset.label === activeDatasetLabel) {
            // swap out the labels and colors in arguments
            argument.labels = dataset.points.map(function (point) { return point.value; });
            argument.legendColors = dataset.points.map(function (point) {
                return {
                    fill: point._saved.fillColor || point.fillColor,
                    stroke: point._saved.strokeColor || point.strokeColor
                };
            });
            argument.title = activeDatasetLabel;
            // position it near the active point
            argument.y = myChart.activeElements[0].y;
        }
    })
    return new originalMultiTooltip(arguments[0]);
}

// this distance function returns the square of the distance if within detection range, otherwise it returns Infinity
var distance = function (chartX, chartY) {
    var hitDetectionRange = this.hitDetectionRadius + this.radius;
    var distance = Math.pow(chartX - this.x, 2) + Math.pow(chartY - this.y, 2);
    return (distance < Math.pow(hitDetectionRange, 2)) ? distance : Infinity;
}
myChart.getPointsAtEvent = function (e) {
    var pointsArray = [],
        eventPosition = Chart.helpers.getRelativePosition(e);

    var leastDistance = Infinity;
    Chart.helpers.each(myChart.datasets, function (dataset) {
        Chart.helpers.each(dataset.points, function (point) {
            // our active point is the one closest to the hover event
            var pointDistance = distance.call(point, eventPosition.x, eventPosition.y)
            if (isFinite(pointDistance) && pointDistance < leastDistance) {
                leastDistance = pointDistance;
                pointsArray = [ point ];
            }
        });
    }, myChart);

    return pointsArray;
}

It does 2 things

  • Replaces the getPointsAtEvent to just pick one point
  • Wraps the MultiTooltip constructor to swap out the list of values passed with all the values from the active point’s series.

Fiddle – http://jsfiddle.net/h93pyavk/

πŸ‘:0

If you extend the line chart, use the code I have above, and the code I pasted below you can get the desired effect to some degree.

    showTooltip: function(ChartElements, forceRedraw) { //custom edit

        //we will get value from ChartElements (which should be only 1 element long in this case) and use it to match the line row we want to see.
        try {
            var numMatch = ChartElements[0].value;
        }
        catch(err) {
            var isChanged = (function(Elements) {
                var changed = true;
                return changed;
            }).call(this, ChartElements);
        }

        // Only redraw the chart if we've actually changed what we're hovering on.
        if (typeof this.activeElements === 'undefined') this.activeElements = [];

        var isChanged = (function(Elements) {
            var changed = false;

            if (Elements.length !== this.activeElements.length) {
                changed = true;
                return changed;
            }

            helpers.each(Elements, function(element, index) {
                if (element !== this.activeElements[index]) {
                    changed = true;
                }
            }, this);
            return changed;
        }).call(this, ChartElements);

        if (!isChanged && !forceRedraw) {
            return;
        } else {
            this.activeElements = ChartElements;
        }
        this.draw();
        if (this.options.customTooltips) {
            this.options.customTooltips(false);
        }
        if (ChartElements.length > 0) {
            // If we have multiple datasets, show a MultiTooltip for all of the data points at that index
            if (this.datasets && this.datasets.length > 1) {
                var dataArray,
                    dataIndex;

                for (var i = this.datasets.length - 1; i >= 0; i--) {
                    dataArray = this.datasets[i].points || this.datasets[i].bars || this.datasets[i].segments;
                    dataIndex = helpers.indexOf(dataArray, ChartElements[0]);
                    if (dataIndex !== -1) {
                        break;
                    }
                }

                var eleLast = "";
                var eleFirst = "";
                var tooltipLabels = [],
                    tooltipColors = [],
                    medianPosition = (function(index) {

                        // Get all the points at that particular index
                        var Elements = [],
                            dataCollection,
                            xPositions = [],
                            yPositions = [],
                            xMax,
                            yMax,
                            xMin,
                            yMin;
                        helpers.each(this.datasets, function(dataset) {
                            dataCollection = dataset.points || dataset.bars || dataset.segments;
                            //console.log(dataset);
                            for(i = 0; i < dataset.points.length; i++) {
                                if(dataset.points[i].value === numMatch) {
                                    for(var k = 0; k < dataset.points.length; k++) {
                                        Elements.push(dataset.points[k]);
                                    }
                                }
                            }
                        });

                        //save elements last label string
                        eleLast = Elements[Elements.length-1].label;
                        eleFirst = Elements[0].label;

                        //console.log(Elements);
                        helpers.each(Elements, function(element) {
                            if(element.value === numMatch) {
                                xPositions.push(element.x);
                                yPositions.push(element.y);
                            }


                            //Include any colour information about the element
                            tooltipLabels.push(helpers.template(this.options.multiTooltipTemplate, element));
                            tooltipColors.push({
                                fill: element._saved.fillColor || element.fillColor,
                                stroke: element._saved.strokeColor || element.strokeColor
                            });

                        }, this);

                        yMin = helpers.min(yPositions);
                        yMax = helpers.max(yPositions);

                        xMin = helpers.min(xPositions);
                        xMax = helpers.max(xPositions);

                        return {
                            x: (xMin > this.chart.width / 2) ? xMin : xMax,
                            y: (yMin + yMax) / 2
                        };
                    }).call(this, dataIndex);

                var newLabel = eleFirst + " to " + eleLast;

                new Chart.MultiTooltip({
                    x: medianPosition.x,
                    y: medianPosition.y,
                    xPadding: this.options.tooltipXPadding,
                    yPadding: this.options.tooltipYPadding,
                    xOffset: this.options.tooltipXOffset,
                    fillColor: this.options.tooltipFillColor,
                    textColor: this.options.tooltipFontColor,
                    fontFamily: this.options.tooltipFontFamily,
                    fontStyle: this.options.tooltipFontStyle,
                    fontSize: this.options.tooltipFontSize,
                    titleTextColor: this.options.tooltipTitleFontColor,
                    titleFontFamily: this.options.tooltipTitleFontFamily,
                    titleFontStyle: this.options.tooltipTitleFontStyle,
                    titleFontSize: this.options.tooltipTitleFontSize,
                    cornerRadius: this.options.tooltipCornerRadius,
                    labels: tooltipLabels,
                    legendColors: tooltipColors,
                    legendColorBackground: this.options.multiTooltipKeyBackground,
                    title: newLabel,
                    chart: this.chart,
                    ctx: this.chart.ctx,
                    custom: this.options.customTooltips
                }).draw();

            } else {
                helpers.each(ChartElements, function(Element) {
                    var tooltipPosition = Element.tooltipPosition();
                    new Chart.Tooltip({
                        x: Math.round(tooltipPosition.x),
                        y: Math.round(tooltipPosition.y),
                        xPadding: this.options.tooltipXPadding,
                        yPadding: this.options.tooltipYPadding,
                        fillColor: this.options.tooltipFillColor,
                        textColor: this.options.tooltipFontColor,
                        fontFamily: this.options.tooltipFontFamily,
                        fontStyle: this.options.tooltipFontStyle,
                        fontSize: this.options.tooltipFontSize,
                        caretHeight: this.options.tooltipCaretSize,
                        cornerRadius: this.options.tooltipCornerRadius,
                        text: helpers.template(this.options.tooltipTemplate, Element),
                        chart: this.chart,
                        custom: this.options.customTooltips
                    }).draw();
                }, this);
            }
        }
        return this;
    },

Obviously this is just a quick and dirty fix, if I get more time to work on it I would like to have each data point show its corresponding value above it.enter image description here

Leave a comment