Search code examples
javascripthighcharts

Placing label within the packed bubble - highcharts


I have a packed bubble chart and here is the jsfiddle: https://jsfiddle.net/ytvcjzx1/

Here is the code:

(function(H) {
  H.addEvent(H.Series, 'afterDrawDataLabels', function(e) {
    const series = this;
    const chart = series.chart;

    if (!series.parentNode.plotX || chart.options.chart.type !== 'packedbubble') {
      return;
    }

    if (!series.customLabel) {
      series.customLabel = series.chart.renderer.label(
          series.name, null, null, 'rect'
        )
        .attr({
          align: 'center',
          fill: 'rgba(255, 255, 255, 0.7)',
          zIndex: 3
        })
        .css({
          color: 'black',
          fontSize: '12px',
          fontWeight: 'bold',
          whiteSpace: 'nowrap', // To prevent text wrapping
          padding: '5px', // Add some padding around the label text
          borderRadius: '5px' // Add rounded corners to the background
        })
        .add();
    }

    series.customLabel.attr({
      x: series.parentNode.plotX + chart.plotLeft,
      y: series.parentNode.plotY + chart.plotTop - 10
    });
  });
}(Highcharts));

Highcharts.chart('container', {
  chart: {
    type: 'packedbubble',
    height: '100%'
  },
  title: {
    text: 'Carbon emissions around the world (2014)',
    align: 'left'
  },
  tooltip: {
    useHTML: true,
    pointFormat: '<b>{point.name}:</b> {point.value}m CO<sub>2</sub>'
  },
  plotOptions: {
    packedbubble: {
      minSize: '20%',
      maxSize: '100%',
      zMin: 0,
      zMax: 1000,
      layoutAlgorithm: {
        gravitationalConstant: 0.05,
        splitSeries: true,
        seriesInteraction: false,
        dragBetweenSeries: true,
        parentNodeLimit: true
      },
      dataLabels: {
        enabled: true,
        format: '{point.name}',
        filter: {
          property: 'y',
          operator: '>',
          value: 250
        },
        style: {
          color: 'black',
          textOutline: 'none',
          fontWeight: 'normal'
        }
      }
    }
  },
  series: [{
    name: 'Europe',
    data: [{
        name: 'Germany',
        value: 767.1
      }, {
        name: 'Croatia',
        value: 20.7
      },
      {
        name: 'Belgium',
        value: 97.2
      },
      {
        name: 'Czech Republic',
        value: 111.7
      },
      {
        name: 'Netherlands',
        value: 158.1
      },
      {
        name: 'Spain',
        value: 241.6
      },
      {
        name: 'Ukraine',
        value: 249.1
      },
      {
        name: 'Poland',
        value: 298.1
      },
      {
        name: 'France',
        value: 323.7
      },
      {
        name: 'Romania',
        value: 78.3
      },
      {
        name: 'United Kingdom',
        value: 415.4
      }, {
        name: 'Turkey',
        value: 353.2
      }, {
        name: 'Italy',
        value: 337.6
      },
      {
        name: 'Greece',
        value: 71.1
      },
      {
        name: 'Austria',
        value: 69.8
      },
      {
        name: 'Belarus',
        value: 67.7
      },
      {
        name: 'Serbia',
        value: 59.3
      },
      {
        name: 'Finland',
        value: 54.8
      },
      {
        name: 'Bulgaria',
        value: 51.2
      },
      {
        name: 'Portugal',
        value: 48.3
      },
      {
        name: 'Norway',
        value: 44.4
      },
      {
        name: 'Sweden',
        value: 44.3
      },
      {
        name: 'Hungary',
        value: 43.7
      },
      {
        name: 'Switzerland',
        value: 40.2
      },
      {
        name: 'Denmark',
        value: 40
      },
      {
        name: 'Slovakia',
        value: 34.7
      },
      {
        name: 'Ireland',
        value: 34.6
      },
      {
        name: 'Croatia',
        value: 20.7
      },
      {
        name: 'Estonia',
        value: 19.4
      },
      {
        name: 'Slovenia',
        value: 16.7
      },
      {
        name: 'Lithuania',
        value: 12.3
      },
      {
        name: 'Luxembourg',
        value: 10.4
      },
      {
        name: 'Macedonia',
        value: 9.5
      },
      {
        name: 'Moldova',
        value: 7.8
      },
      {
        name: 'Latvia',
        value: 7.5
      },
      {
        name: 'Cyprus',
        value: 7.2
      }
    ]
  }, {
    name: 'Long word 1234432222 1234432222 1234432222 1234432222 1234432222',
    data: [{
        name: 'Senegal',
        value: 8.2
      },
      {
        name: 'Cameroon',
        value: 9.2
      },
      {
        name: 'Zimbabwe',
        value: 13.1
      },
      {
        name: 'Ghana',
        value: 14.1
      },
      {
        name: 'Kenya',
        value: 14.1
      },
      {
        name: 'Sudan',
        value: 17.3
      },
      {
        name: 'Tunisia',
        value: 24.3
      },
      {
        name: 'Angola',
        value: 25
      },
      {
        name: 'Libya',
        value: 50.6
      },
      {
        name: 'Ivory Coast',
        value: 7.3
      },
      {
        name: 'Morocco',
        value: 60.7
      },
      {
        name: 'Ethiopia',
        value: 8.9
      },
      {
        name: 'United Republic of Tanzania',
        value: 9.1
      },
      {
        name: 'Nigeria',
        value: 93.9
      },
      {
        name: 'South Africa',
        value: 392.7
      }, {
        name: 'Egypt',
        value: 225.1
      }, {
        name: 'Algeria',
        value: 141.5
      }
    ]
  }
  ]
});

You notice the series name Long word 1234432222 1234432222 1234432222 1234432222 1234432222 is well outside the bubble. Can we enable text overflow and make sure all series name are displayed only within the bubble (like adding the ellipsis ...), for example the text can be Long word 1234432222... so that user can hover and find its full version via the tooltip? Or is it possible via css?


Solution

  • To make the custom label appear inside the bubble you could try this modification to your rendered label:

    series.customLabel = series.chart.renderer.label(
          series.name, null, null, 'rect'
        )
        .attr({
          align: 'center',
          fill: 'rgba(255, 255, 255, 0.7)',
          zIndex: 3
        })
        .css({
          color: 'black',
          fontSize: '12px',
          fontWeight: 'bold',
          whiteSpace: 'nowrap', // To prevent text wrapping
          padding: '5px', // Add some padding around the label text
          borderRadius: '5px', // Add rounded corners to the background
          textOverflow: 'ellipsis',
          width: series.parentNode.graphic.width
        })
        .add();
    

    Which adds textOverflow: 'ellipsis', width: series.parentNode.graphic.width.

    See this initial JSFiddle demonstration, or this JSFiddle demonstration that has some padding and handles window resizing.