Hendrik Hendrik - 10 days ago 5
CoffeeScript Question

ChartJS display tooltip at maximum/minimum value

I would like to display a tooltip at the top datapoint of a dataset in my chart. That way the user sees a value for the maximum and the minimum datapoints. That helps understanding the graph immediately.

It should look somewhat like this:
expected end result for the user

I have created a JSFiddle with the code over here: https://jsfiddle.net/h2zscw6s/1/

For reference please also find my chart config below. It's in coffeescript but I don't mind if you post answers in plain javascript.

dataWeather = [
{
label: 'January'
value: 22
}
{
label: 'February'
value: 23
}
{
label: 'March'
value: 24
}
{
label: 'May'
value: 26
}
{
label: 'June'
value: 30
}
{
label: 'July'
value: 34
}
{
label: 'August'
value: 38
}
{
label: 'September'
value: 36
}
{
label: 'October'
value: 30
}
{
label: 'November'
value: 28
}
{
label: 'December'
value: 26
}
]
dataPrices = [
{
label: 'January'
value: 5000
}
{
label: 'February'
value: 4500
}
{
label: 'March'
value: 4450
}
{
label: 'May'
value: 3700
}
{
label: 'June'
value: 3700
}
{
label: 'July'
value: 3000
}
{
label: 'August'
value: 2900
}
{
label: 'September'
value: 3100
}
{
label: 'October'
value: 3200
}
{
label: 'November'
value: 3900
}
{
label: 'December'
value: 5500
}
]

class WeatherPriceChart
setWeatherData: (weatherData)->
@weatherData = weatherData
setPriceData: (priceData)->
@priceData = priceData
minPrice: ->
_.sortBy(@priceData, (w)-> w.value)[0]?.value || 0
maxPrice: ->
_.sortBy(@priceData, (w)-> -w.value)[0]?.value || 0
minTemperature: ->
_.sortBy(@weatherData, (w)-> w.value)[0]?.value || 0
maxTemperature: ->
_.sortBy(@weatherData, (w)-> -w.value)[0]?.value || 0
isMaxTemperature: (value)->
@maxTemperature() == value
isMinTemperature: (value)->
@minTemperature() == value
isMaxPrice: (value)->
@maxPrice() == value
isMinPrice: (value)->
@minPrice() == value
getLabels: ->
_.map(@weatherData, (w)-> w.label)
getWeatherDataPoints: ->
_.map(@weatherData, (w)-> w.value)
getPriceDataPoints: ->
_.map(@priceData, (w)-> w.value)
getNormalizedWeatherDataPoints: ->
data = @weatherData
min = -10
max = 60
_.map data, (d)->
norm = d.value + (min * -1)
maxed = max + (min * -1)
norm / maxed * 100
getNormalizedPriceDataPoints: ->
data = @priceData
max = @maxPrice() * 2.5
_.map data, (d)->
d.value / max * 100


chart = new WeatherPriceChart
chart.setWeatherData(dataWeather)
chart.setPriceData(dataPrices)

ctx = document.getElementById('myChart')
myChart = new Chart(ctx,
type: 'line',
data:
xLabels: chart.getLabels(),
yLabels: [""],
datasets: [
{
label: 'Temperature'
data: chart.getWeatherDataPoints()
backgroundColor: 'rgba(239,88,42,0.2)'
borderColor: 'rgba(239,88,42,0.5)'
borderWidth: 1
},
{
label: 'Prices'
data: chart.getNormalizedPriceDataPoints()
backgroundColor: 'rgba(22,195,245,0.2)'
borderColor:'rgba(22,195,245,0.4)'
borderWidth: 1
}
]
options:
scales:
yAxes: [
{
ticks: {
beginAtZero: false,
display: false
},
display: false
},
]
legend:
display: false
)

Answer

I found an answer to this option and posted it in the linked jsfiddle. I had to add a plugin that would display all labels. That one then dynamically decided whether to show this tooltip or not.

Chart.pluginService.register
  beforeRender: (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 (dataset, i) ->
        min = _.min(dataset.data, (d)-> d)
        max = _.max(dataset.data, (d)-> d)
        minShown = false
        maxShown = false
        chart.getDatasetMeta(i).data.forEach (sector, j) ->
          isMax = dataset.data[j] == max
          isMin = dataset.data[j] == min
          # only show the min and the max once. we can have duplicates min/maxes
          if isMax && !maxShown || isMin && !minShown
            minShown = true if isMin
            maxShown = true if isMax
            tooltip = new (Chart.Tooltip)({
              _chart: chart.chart
              _chartInstance: chart
              _data: chart.data
              _options: chart.options.tooltips
              _active: [ sector ]
            }, chart)
            chart.pluginTooltips.push(tooltip)
          return
        return
      # turn off normal tooltips
      chart.options.tooltips.enabled = false
    return
  afterDraw: (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, (tooltip) ->
        tooltip.initialize()
        tooltip.update()
        # we don't actually need this since we are not animating tooltips
        tooltip.pivot()
        tooltip.transition(easing).draw()
        return
      chart.options.tooltips.enabled = false
    return
Comments