cancel
Showing results for 
Search instead for 
Did you mean: 

Bar Chart - Prevent bars from hiding the value label text

Ophir_Buchman
12 - Data Integration
12 - Data Integration

When creating a bar chart and formatting it to include "Values",  you might find out that the bars cover their values. The following (widget) code manipulates the bars' width and label position:

const labelWidth = 25 + 5 // 5 is the space between the label and the bar

widget.on('domready', function(widget) {
try {
console.log('DOM Ready script starting (wid=' + widget.oid + ')');

// Get plot width
plotWidth = parseInt(widget.chart[0].getElementsByClassName('highcharts-plot-border')[0].getAttribute('width'));

// Find Widest bar
maxBarWidth = -1;
widget.chart[0].getElementsByClassName('highcharts-series','highcharts-bar-series').forEach(bar => {
bar.childNodes.forEach(rect => {
if (parseInt(rect.getAttribute('height')) > maxBarWidth)
maxBarWidth = parseInt(rect.getAttribute('height'))
})
})

if (maxBarWidth == -1) return;

// Check if we require adjustment
if (plotWidth - maxBarWidth > labelWidth) return;

ratio = (maxBarWidth - labelWidth) / maxBarWidth;

// Adjust Bar height and location
barsArray = widget.chart[0].getElementsByClassName('highcharts-series','highcharts-bar-series');
labelsArray = widget.chart[0].getElementsByClassName('highcharts-data-labels');

for (let seriesNum = 0; seriesNum < barsArray.length; seriesNum++) {
for (let barNum = 0; barNum < barsArray[seriesNum].childNodes.length; barNum++) {
currentWidth = parseInt(barsArray[seriesNum].childNodes[barNum].getAttribute('height'));
currentY = parseInt(barsArray[seriesNum].childNodes[barNum].getAttribute('y'));
transformLabel = labelsArray[seriesNum].childNodes[barNum].getAttribute('transform')

// Calculate offset
offset = Math.round(currentWidth * (1-ratio))

// Adjust bars
barsArray[seriesNum].childNodes[barNum].setAttribute('height',currentWidth-offset)
barsArray[seriesNum].childNodes[barNum].setAttribute('y',currentY+offset)

// Adjust labels
newLabel = 'translate(' + (currentWidth - offset) + ',' + transformLabel.split(',')[1]
labelsArray[seriesNum].childNodes[barNum].setAttribute('transform',newLabel)
}
}
}
catch (error) {
console.error(error);
}
finally {
console.log('DOM Ready script complete (wid=' + widget.oid + ')');
}
});
Before After
Ophir_Buchman_0-1648199914908.png Ophir_Buchman_1-1648199963769.png
1 ACCEPTED SOLUTION

TriAnthony
Community Team Member
Community Team Member

Hi @fabricio_fta and @Jake_Raz, please try this script below. This should work for:

  • all Cartesian charts (Column, Bar, Line, and Area charts),
  • negative values,
  • single and multiple series (classic or stacked, using Break By or multiple values).

 

/***  This script is for increasing the y-axis max value by adding a percent of the highest value in the series to the y-axis max value.
Similarly, in cases where the minimum value is negative, it also decreases the y-axis min value by subtracting a percent of the lowest value in the series from the y-axis min value. ***/

//Set the offset percent to add/subtract to/from the y-axis max/min value here
var percentToAdd = 20;

widget.on('processresult', function(se,ev){

	var numberOfSeries = ev.result.series.length;

	var seriesData = [];

	var dataPoints = [];

	for(i=0; i < numberOfSeries; i++) {

		seriesData[i] = ev.result.series[i].data;

		var numOfDataPoints = seriesData[i].length;

		if(widget.subtype.includes('stacked')) {

			if(i==0) {
				for(j=0; j < numOfDataPoints; j++) {

					var dataToPush = seriesData[i][j].y;

					dataPoints.push(dataToPush);
				}
			}
			else {
				for(j=0; j < numOfDataPoints; j++) {

					dataPoints[j] = dataPoints[j] + seriesData[i][j].y;
				}
			}
		}

		else if(widget.subtype.includes('classic') || widget.subtype.includes('basic')) {

			for(j=0; j < numOfDataPoints; j++) {

				var dataToPush = seriesData[i][j].y;

				dataPoints.push(dataToPush);
			}
		}
	}

	var maxValue = Math.max.apply(Math, dataPoints);

	var minValue = Math.min.apply(Math, dataPoints);


	if(ev.result.yAxis[0].max <= 0) {
		ev.result.yAxis[0].max = 0
	}
	else {
		ev.result.yAxis[0].max = maxValue * (1 + percentToAdd/100);
	}

	if(ev.result.yAxis[0].min >= 0) {
		ev.result.yAxis[0].min = 0;
	}
	else {
		ev.result.yAxis[0].min = minValue * (1 + percentToAdd/100);
	}


})

 

Tri Anthony Situmorang

View solution in original post

9 REPLIES 9

cartercjb
10 - ETL
10 - ETL

@Ophir_Buchman how do you apply fill-opacity to the bars only so that it does not apply to the data labels as well?

Ophir_Buchman
12 - Data Integration
12 - Data Integration

Hi @cartercjb 

In 'domready' - Perform the following:

$('.highcharts-series.highcharts-series-1')[0].childNodes.forEach(item => item.setAttribute('opacity',0.3))

 

Ophir_Buchman_0-1651047362377.png

 

BIQ
8 - Cloud Apps
8 - Cloud Apps

How can we get this code to work on column charts as well? 

fabricio_fta
7 - Data Storage
7 - Data Storage

How this code could also work for negative bar charts? For us, it's working fine for positive values, but for negative values, it does not.

Example:

fabricio_fta_0-1692131088960.png

We would like that the numbers once inside the bar would be white as the third bar is and once they out they would be gray the first bar is.

Jake_Raz
10 - ETL
10 - ETL

Hello! This script doesn't seem to work on stacked bar charts. On regular (non-stacked) bar charts it mostly works as expected, and I can adjust the number in the first line to adjust how much space there is. However, when I try the same script on stacked bar charts, it doesn't seem to have any effect, regardless of which number I put in the first line. 

I bet it's due to the break-by splitting the bars into multiple stacked bars, and the script is only designed to shorten single bars. Is there a way to modify the script so it works on stacked bars instead?

TriAnthony
Community Team Member
Community Team Member

Hi @fabricio_fta and @Jake_Raz, please try this script below. This should work for:

  • all Cartesian charts (Column, Bar, Line, and Area charts),
  • negative values,
  • single and multiple series (classic or stacked, using Break By or multiple values).

 

/***  This script is for increasing the y-axis max value by adding a percent of the highest value in the series to the y-axis max value.
Similarly, in cases where the minimum value is negative, it also decreases the y-axis min value by subtracting a percent of the lowest value in the series from the y-axis min value. ***/

//Set the offset percent to add/subtract to/from the y-axis max/min value here
var percentToAdd = 20;

widget.on('processresult', function(se,ev){

	var numberOfSeries = ev.result.series.length;

	var seriesData = [];

	var dataPoints = [];

	for(i=0; i < numberOfSeries; i++) {

		seriesData[i] = ev.result.series[i].data;

		var numOfDataPoints = seriesData[i].length;

		if(widget.subtype.includes('stacked')) {

			if(i==0) {
				for(j=0; j < numOfDataPoints; j++) {

					var dataToPush = seriesData[i][j].y;

					dataPoints.push(dataToPush);
				}
			}
			else {
				for(j=0; j < numOfDataPoints; j++) {

					dataPoints[j] = dataPoints[j] + seriesData[i][j].y;
				}
			}
		}

		else if(widget.subtype.includes('classic') || widget.subtype.includes('basic')) {

			for(j=0; j < numOfDataPoints; j++) {

				var dataToPush = seriesData[i][j].y;

				dataPoints.push(dataToPush);
			}
		}
	}

	var maxValue = Math.max.apply(Math, dataPoints);

	var minValue = Math.min.apply(Math, dataPoints);


	if(ev.result.yAxis[0].max <= 0) {
		ev.result.yAxis[0].max = 0
	}
	else {
		ev.result.yAxis[0].max = maxValue * (1 + percentToAdd/100);
	}

	if(ev.result.yAxis[0].min >= 0) {
		ev.result.yAxis[0].min = 0;
	}
	else {
		ev.result.yAxis[0].min = minValue * (1 + percentToAdd/100);
	}


})

 

Tri Anthony Situmorang

This seems to work! Thanks!