1

I was successfully established my flot chart, based on this previous post

I want to enable to viewer to show/hide the series with a click. I found bunch of solutions, both official and others, but none of those worked for me. I'll explain:

  1. Official turning series on/off: this works, but looks very messy as the legend is eventually duplicated twice (disappears from official legend once the series turned off).
  2. Hiddengraphs.js: this is a plugin which can be found at the plugin repository, but it doesn't work and interact well with Chrome (tried more than one machine, it just don't work).
  3. This solution is actually really nice (I don't mind that there are no checkboxes to check), but when I integrated it into my code, all I got was "jumping" to the top of the page, and nothing happens.
  4. Lastly, I found this solution, which also works, altough using another js library, called flot.togglelegend.js. In this implementation I found some major conflicts with flot.cust.js, and couldn't get them both to work together.

Here's my current js (written in coffeescript)

    colorArray = []
    colorArray.push "rgba(180, 0, 75,    0.6)"
    colorArray.push "rgba(0, 150, 100,   0.6)"
    colorArray.push "rgba(0, 0, 255,     0.6)"
    colorArray.push "rgba(140, 0, 255,   0.6)"
    colorArray.push "rgba(90, 180, 20,   0.6)"
    colorArray.push "rgba(255, 236, 0,   0.6)"
    colorArray.push "rgba(234, 170, 21,  0.6)"
    colorArray.push "rgba(95, 180, 190,  0.6)"
    colorArray.push "rgba(214, 92, 63,   0.6)"
    colorArray.push "rgba(218, 106, 234, 0.6)"
    colorArray.push "rgba(213, 128, 155, 0.6)"

    # chart colors default 
    $chrt_border_color = "#efefef"
    $chrt_grid_color = "#DDD"
    $chrt_main = "#E24913"

    # red       
    $chrt_second = "#6595b4"
    # blue      
    $chrt_third = "#FF9F01"
    # orange    
    $chrt_fourth = "#7e9d3a"
    # green     
    $chrt_fifth = "#BD362F"
    # dark red  
    $chrt_mono = "#000"

    Chart = 

    generateDataObjects: (all_series, all_series_data) ->
        plotData = []

        for series, i in all_series
            obj =
                label: series.replace /__/g, "|"
                data: all_series_data[i]
                color: colorArray[i]

            plotData.push obj

        return plotData

    togglePlot: (seriesIdx) ->
        someData = plot.getData()
        someData[seriesIdx].lines.show = not someData[seriesIdx].lines.show
        plot.setData someData
        plot.draw()
        return  

    getTooltip: (label, xval, yval, flotItem) ->
            return 'Build: <span>'+ flotItem.series.data[flotItem.dataIndex][6]+'</span>' +" |     Run ID: <strong> #{flotItem.series.data[flotItem.dataIndex][7].toString()}</strong>" + '<br> Result: <span>'+Chart.commify(yval)+'</span>'

    commify: (x) ->
        return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");

    generateChartOptions: (legend_container, ticks) ->
        return (
            series:
                lines:
                    show: true

                points:
                    show: true

            crosshair:
                mode: "x"

            legend:
                container: $("##{legend_container}")
                labelFormatter: (label, series) ->
                    "<a href=\"javascript:void(0);\" onClick=\"Chart.togglePlot(" + series.idx + "); return false;\">" + label + "</a>"
                noColumns: 4
                # hideable: true

            grid:
              hoverable: true
              clickable: true
              tickColor: $chrt_border_color
              borderWidth: 0
              borderColor: $chrt_border_color

            tooltip: true
            tooltipOpts: 
              content : Chart.getTooltip
              #content : "Value <b>$x</b> Value <span>$y</span>",
              defaultTheme: false

            xaxis:
                ticks: ticks
                rotateTicks: 30

            selection:
                mode: "xy"
            )



     jQuery ->
        if $("#normalized_bw_chart").length         # render only if the chart-id is present

            raw_data = $("#normalized_bw_chart").data('results')
            ticks = $("#normalized_bw_chart").data('ticks')
            all_series = $("#normalized_bw_chart").data('series')

            plot = $.plot($("#normalized_bw_chart"), Chart.generateDataObjects(all_series, raw_data), Chart.generateChartOptions('normalized_bw_legend', ticks))    

        if $("#concurrent_flows_chart").length      # render only if the chart-id is present

            raw_data = $("#concurrent_flows_chart").data('results')
            ticks = $("#concurrent_flows_chart").data('ticks')
            all_series = $("#concurrent_flows_chart").data('series')

            plot = $.plot($("#concurrent_flows_chart"), Chart.generateDataObjects(all_series, raw_data), Chart.generateChartOptions('concurrent_flows_legend', ticks))

        if $("#bandwidth_chart").length         # render only if the chart-id is present

            raw_data = $("#bandwidth_chart").data('results')
            ticks = $("#bandwidth_chart").data('ticks')
            all_series = $("#bandwidth_chart").data('series')

            plot = $.plot($("#bandwidth_chart"), Chart.generateDataObjects(all_series, raw_data), Chart.generateChartOptions('bandwidth_legend', ticks))    

        $("[data-behavior~=chart-selection]").bind "plotselected", (event, ranges) ->
                selected_chart = $(this).attr('id')[0...-6] # slicing the name of the selected item
                console.log  ("zooming in to " + selected_chart)
                plot = $.plot($("##{selected_chart}_chart"), plot.getData(), $.extend(true, {}, Chart.generateChartOptions(selected_chart+'_legend', ticks),
                  xaxis:
                    min: ranges.xaxis.from
                    max: ranges.xaxis.to

                  yaxis:
                    min: ranges.yaxis.from
                    max: ranges.yaxis.to
                ))
             return

        $("[data-behavior~=chart-selection]").bind "dblclick", (event, pos, item) ->
                selected_chart = $(this).attr('id')[0...-6] # slicing the name of the selected item
                console.log  ("zooming out to " + selected_chart)
                plot = $.plot($("##{selected_chart}_chart"), plot.getData(), $.extend(true, {}, Chart.generateChartOptions(selected_chart+'_legend', ticks),
                  xaxis:
                    min: null
                    max: null
              yaxis:
                min: null
                max: null
            ))
         return

here's a fiddle: http://jsfiddle.net/danklein/0tn1uzs9/3/

thanks a lot!

Community
  • 1
  • 1
cyber101
  • 899
  • 1
  • 9
  • 19
  • 1
    Solution 3. should be simple. It sounds like the `onClick` part in the `labelFormatter` is incorrect. Could you give a fiddle or code snippet which shows your implementation of this? – Raidri Sep 24 '14 at 11:23
  • 1
    I agree with @Raidri, solution 3 (which orginally came from this question: http://stackoverflow.com/questions/14201911/dynamic-flot-graph-show-hide-series-by-clicking-on-legend-text-or-box-on-graph/14227287#14227287) is the most straightforward way I've seen to do this. The fact that it doesn't work for you just sounds like a bug in your code. Also, if desired, it would also be easy to add in checkboxes... – Mark Sep 24 '14 at 12:50
  • @Mark, Raidiri, I assume this is indeed the problem but I can't figure out why the Chart.togglePlot isn't reached. I Updated my code with my most updated try... Note that I've changed the `#` to `javascript:void(0);` so that the page won't "jump" to its start and reload every time the series link is clicked (as I mentioned earlier) – cyber101 Sep 24 '14 at 13:46
  • Shouldn't it be `plot.setData(someData)` instead of `plot.setData someData` in your togglePlot function? (Or is that a CoffeScript thing? I only do plain JS.) More to the point: Are there errors in the console? Have you tried stepping through the code? Finding the error only through looking at the code is a bit tedious ... – Raidri Sep 24 '14 at 14:03
  • @Raidri, I get the following from the console: `Uncaught ReferenceError: Chart is not defined (index):1 onclick (index):1` Which basically says that my method isn't reachable. – cyber101 Sep 24 '14 at 14:10
  • Hm, I would have to see the generated javascript as a code snippet or fiddle to help you further. – Raidri Sep 24 '14 at 14:18
  • I added a fiddle- [http://jsfiddle.net/danklein/0tn1uzs9/3/](http://jsfiddle.net/danklein/0tn1uzs9/3/) This is forked from @Ryley solution to my original question, so the credit is his :) – cyber101 Sep 24 '14 at 14:21

1 Answers1

2

1) The onClick directly in the HTML is a bad idea when the scope of the Chart object is not global. I changed it to a jquery event handler:

$('body').on 'click', 'a.legendtoggle', (event) ->
    Chart.togglePlot($(this).data('index'))
    return false

2) The series object in the labelFormatter function has no idx property, so I used a variable inside the Chart object:

labelFormatter: (label, series) ->
    "<a href=\"#\" class=\"legendtoggle\" data-index=\"" + Chart.legendindex++ + "\">" + label + "</a>"

3) I also put your plot object inside Chart so that it can be accessed inside the togglePlot function. And I changed the lines to points since your data has only one datapoint per series:

togglePlot: (seriesIdx) ->
    someData = this.plot.getData()
    someData[seriesIdx].points.show = not someData[seriesIdx].points.show
    this.plot.setData someData
    this.plot.draw()
    return

That should be all I changed, but compare for yourself if I got everything.
Here is a working fiddle: http://jsfiddle.net/jhpgtxz1/2/

PS: Never again CoffeeScript for me :-(

Raidri
  • 17,258
  • 9
  • 62
  • 65
  • This seems awesome!!! thanks! I got `Uncaught TypeError: Cannot read property 'points' of undefined ` I searched a bit and found that the data-index count to start in 2 instead of 0. So, I tried changing the `[seriesIdx]` to `[seriesIdx-2]` and it worked. Also, please note that the fiddle is a mockup for my actual page, which contains three graphs. The other two graphs aren't responding... Finally, I have in my actual chart both lines and dots, is there any way of toggling them together, or should I just run two lines of code, one for each attribute (line, point) – cyber101 Sep 24 '14 at 16:36
  • Do your three graphs use the same legend? If not, you need to seperate the click events for the graphs (different classes or graph no as data attribute e.g.). And yes, you need two lines of code for points and lines. – Raidri Sep 24 '14 at 20:29
  • There is a different legend to each graph (same series, but each legend is placed under different container and should be treated separately. The click event is fetched in all of the graphs but they are all changes the top graph alone. – cyber101 Sep 24 '14 at 20:35
  • can you think of the possibility in which my legendCounter starts in 2 instead of 0? Also, is there any why to fetch and handle all three graphs in a single block of code? With your solution the only thing I think might me missing is adding the toggle somehow the flot container.. When I tried implementing you recommendation using three different listeners, I had the same problem as the `togglePlot` method only fetches the first flot, and the method only receive and seriesIdx as an argument.. – cyber101 Sep 25 '14 at 13:02
  • the entire js code is attached to the original question. There are three flot charts under `bandwidth_chart`, `concurrent_flows_chart`, and `normalized_bw_chart` IDs. There is a corresponding `_legend` id to contain each legend. Each chart has same series names (and same # of series) and same ticks, but each chart is of course with different y-axis values. I have no problem attaching the HTML but i don't think it will help here. – cyber101 Sep 25 '14 at 13:19
  • You assign all plots to the same `plot` variable, that's not going to work. – Raidri Sep 25 '14 at 14:07
  • And it doesn't... But I didn't know what would be the correct way to do that and in the same time avoid code-duplication by using Chart methods. – cyber101 Sep 25 '14 at 14:09
  • How would you best reccommend on approaching this? Can you please provide an example? (I also struggle with that when tried to implement a selection+zooming to each of the chart alone, but I got similar effect where one chart changes another one). I tried doing so by fetching a data-attribute, such as `$("[data-behavior~=chart-selection]").bind` but unfortunately didn't get it to work... – cyber101 Sep 25 '14 at 17:25
  • This discussion is getting too long already... data attributes should help, but I would have to see the actual implementation. Please create a new question (concentrating on the issue with multiple plots on one page) with a complete code example if you need more help. – Raidri Sep 26 '14 at 08:26
  • Thanks! I have created a new question concerning the handling of multiple flots in a single html page- [here](http://stackoverflow.com/questions/26056722/flot-charts-handling-multiple-flots-in-a-single-html-page) – cyber101 Sep 26 '14 at 09:51