Search code examples
javascripthighchartstogglevisiblesankey-diagram

How to toggle visible the Highcharts Sankey link with javascript


How can I toggle visible the Sankey link (together with the node and label)? I want users to expand nodes step by step by clicking on the node. The diagram would have ca 5 levels and ca 10 columns when fully expanded. Default view on the first load is showing about 6 columns and 4 levels.

  1. I could manipulate the data and set the weight to 0 to hide all 3 of them (node,link,label).
  2. I could add a className to the node and use 'display:none' (Would it work for all 3 of them?)
  3. Here is a similar question about organization chart. Can I adapt the solution to Sankey?

I've tried out the 1st option. It works well - hiding all 3 of them(node,link,label). Though, to make it to work, I need to query the database every time and redraw (reload) the chart. I'm doing API call and manipulating the json. Isn't it too many API calls or there is a better way to do it?

The 2nd option looks more clear compared to the 3rd, because I know more CSS than highcharts.js. (Though I tend to avoid the cascading styles:)

The 3rd option seems tedious, because I need to find out how these libraries are working. But maybe it's a good solution, then I have no doubts about investing time. Though, the given example was not very well working.

What would be the best solution to avoid problems with dev and customisation, functionalities, export.js, responsive design, etc?

Here is the fiddle of the desired view : link

var nodes = [{
    id: 'Col_1-Row_1',
    column: 1
  }, ],
  data0 = [
    // default - data is set to 0 - link is hidden
    ['Col_0-Row_0', 'Col_1-Row_0', 0],
    ['Col_0-Row_1', 'Col_1-Row_0', 0],
    // data to display
    ['Col_1-Row_0', 'Col_2-Row_0', 153],
    ['Col_1-Row_1', 'Col_2-Row_0', 91],
    ['Col_1-Row_1', 'Col_2-Row_0', 221],
    ['Col_1-Row_1', 'Col_2-Row_0', 200],
    //  circulra link
    ['Col_2-Row_0', 'Col_1-Row_1', 10],
  ],
  data1 = [
    ['Col_0-Row_0', 'Col_1-Row_0', 0.1],
    ['Col_0-Row_1', 'Col_1-Row_0', 152.9],
    ['Col_1-Row_0', 'Col_2-Row_0', 153],
    ['Col_1-Row_1', 'Col_2-Row_0', 91],
    ['Col_1-Row_1', 'Col_2-Row_0', 221],
    ['Col_1-Row_1', 'Col_2-Row_0', 200],
    ['Col_2-Row_0', 'Col_1-Row_1', 10],
  ]

const chart = Highcharts.chart('container', {
  chart: {

    height: (9 / 16 * 80) + "%",
    marginBottom: 60, // display circulra link
    marginRight: 60 // display circulra link
  },
  title: {
    text: 'Sankey toggle link visibilty'
  },
  plotOptions: {
    series: {
      animation: false,
      minLinkWidth: 1,
      nodePadding: 50,
      colors: ['#0dcaf0'],
      clip: false, // display circulra link
      dataLabels: {
        enabled: true,

        // set data label position
        align: "left",
        verticalAlign: "top",
        y: -20,
        x: -5,

        // show data labels taht overlap
        allowOverlap: true,
        padding: 0,

        // handle data labels that flow outside the plot area
        overflow: "allow",
        crop: false,

        style: {
          fontSize: "12px",
          fontFamily: "Arial, sans-serif",
          color: "black",
        },
      },
    },
  },
  series: [{
    data: data0,
    nodes: nodes,
    type: 'sankey',
  }, {
    data: data1,
    nodes: nodes,
    visible: false,
    type: 'sankey',
  }]
});
#container {
  height: 90hw;
  margin: 1em auto;
}
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/sankey.src.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/accessibility.js"></script>

<button id="btn_show" class="autocompare">expand</button>
<button id="btn_hide" class="autocompare">hide</button>
<div id="container"></div>


<script>
  document.getElementById('btn_show').addEventListener('click', e => {
    seriesHide();
    seriesShow(1);
  });
  document.getElementById('btn_hide').addEventListener('click', e => {
    seriesHide();
    seriesShow(0);
  });

  function seriesHide() {
    chart.series.forEach(ser => ser.hide())
  }

  function seriesShow(i) {
    var series = chart.series[i];
    series.show();
  }
</script>


Solution

  • I prepared a demo based on a post from SO, hiding branches seems to be a good approach.

    Highcharts.chart('container', {
      chart: {
        events: {
          load() {
            //hide nodes after initial load
            let chart = this,
              nodes = chart.series[0].nodeLookup;
    
            for (let i in nodes) {
              if (nodes[i].column > 1) {
                nodes[i].graphic.hide();
                nodes[i].dataLabel.hide();
                nodes[i].linksTo[0].graphic.hide();
                nodes[i].visible = false;
              }
            }
          }
        }
      },
      title: {
        text: 'Highcharts Sankey Diagram'
      },
      accessibility: {
        point: {
          valueDescriptionFormat: '{index}. {point.from} to {point.to}, {point.weight}.'
        }
      },
      series: [{
        keys: ['from', 'to', 'weight'],
        data: [
          ['Brazil', 'Portugal', 5],
          ['Brazil', 'France', 1],
          ['Brazil', 'Spain', 1],
          ['Brazil', 'England', 1],
          ['Canada', 'Portugal', 1],
          ['Canada', 'France', 5],
          ['Canada', 'England', 1],
          ['Mexico', 'Portugal', 1],
          ['Mexico', 'France', 1],
          ['Mexico', 'Spain', 5],
          ['Mexico', 'England', 1],
          ['USA', 'Portugal', 1],
          ['USA', 'France', 1],
          ['USA', 'Spain', 1],
          ['USA', 'England', 5],
          ['Portugal', 'Angola', 2],
          ['Portugal', 'Senegal', 1],
          ['Portugal', 'Morocco', 1],
          ['Portugal', 'South Africa', 3],
        ],
        type: 'sankey',
        name: 'Sankey demo series',
        nodes: [{
          id: 'Brazil',
        }, {
          id: 'Portugal',
          events: {
            click() {
              //show nodes
              let series = this.series,
                nodes = series.nodeLookup;
    
              for (let i in nodes) {
                if (nodes[i].id === "Portugal") {
                  if (nodes[i].visible) {
                    nodes[i].graphic.hide()
                    nodes[i].dataLabel.hide();
                    nodes[i].visible = false;
                  } else {
                    nodes[i].graphic.show()
                    nodes[i].dataLabel.show();
                    nodes[i].visible = true;
                  }
    
                }
              }
              this.linksFrom.forEach(link => {
                if (link.graphic.visibility == "visible") {
                  link.graphic.hide()
    
                } else {
                  link.graphic.show()
                }
              })
            }
          }
        }, {
          id: 'Mexico',
        }, {
          id: 'France',
        }],
      }]
    
    });
    #csv {
        display: none;
    }
    
    .highcharts-figure,
    .highcharts-data-table table {
        min-width: 310px;
        max-width: 800px;
        margin: 1em auto;
    }
    
    .highcharts-data-table table {
        font-family: Verdana, sans-serif;
        border-collapse: collapse;
        border: 1px solid #ebebeb;
        margin: 10px auto;
        text-align: center;
        width: 100%;
        max-width: 500px;
    }
    
    .highcharts-data-table caption {
        padding: 1em 0;
        font-size: 1.2em;
        color: #555;
    }
    
    .highcharts-data-table th {
        font-weight: 600;
        padding: 0.5em;
    }
    
    .highcharts-data-table td,
    .highcharts-data-table th,
    .highcharts-data-table caption {
        padding: 0.5em;
    }
    
    .highcharts-data-table thead tr,
    .highcharts-data-table tr:nth-child(even) {
        background: #f8f8f8;
    }
    
    .highcharts-data-table tr:hover {
        background: #f1f7ff;
    }
    <script src="https://code.highcharts.com/highcharts.js"></script>
    <script src="https://code.highcharts.com/modules/sankey.js"></script>
    <script src="https://code.highcharts.com/modules/exporting.js"></script>
    <script src="https://code.highcharts.com/modules/export-data.js"></script>
    <script src="https://code.highcharts.com/modules/accessibility.js"></script>
    
    <figure class="highcharts-figure">
        <div id="container"></div>
        <p class="highcharts-description">
            Sankey charts are used to visualize data flow and volume
            between nodes. The wider lines indicate larger volumes.
        </p>
    </figure>

    Demo: https://jsfiddle.net/BlackLabel/sm01jtof/1/

    EDIT

    Updated demo to show/hide nodes in sankey graph using update method.

    Demo: https://jsfiddle.net/BlackLabel/80ruyxdq/4/