0
votes

I'm working with the chartJS library and trying to figure out what I need to do to get a single lines data to display in the tooltip.

For example, Wrong

I am hovering over the blue line here and see every data point at that mark. What I would like to do is see all three data points for the blue line only.

I've made some progress from chart js tooltip how to control the data that show

    getPointsAtEvent: function(e) {
        var pointsArray = [], eventPosition = helpers.getRelativePosition(e);
        var breakLoop = 0;
        helpers.each(this.datasets, function(dataset) {
            helpers.each(dataset.points, function(point) {
                if (point.inRange(eventPosition.x, eventPosition.y) && point.showTooltip && !point.ignore)  {
                    if(eventPosition.y + 2 >= point.y && eventPosition.y - 2 <= point.y) {
                        pointsArray.push(point);
                        breakLoop = 1;
                        return false;
                    }
                }
            });
            if(breakLoop) {
                return false;
            }
        }, this);
        //console.log(pointsArray);
        return pointsArray;
    },

Is my chart modification that will return 1 data point on the graph. I'm assuming the next step is to overwrite the showToolTip method.

2
Look at the documentation. Seems like you could probably manipulate tooltipTemplate to do what you want. Try it out and see where you get.crush
Not sure about that. getPointsAtEvent, is interesting. Some how it seems to take X,Y coordinates from each data point to pick which to display.user3333134

2 Answers

0
votes

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

0
votes

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/