CodeIn House

  • Laravel
  • WordPress
  • jQuery
  • Javascript
  • Contact
Home   D3   Dynamic Vertical Bar Chart With D3 With Labels Using JSON Da ...

Dynamic Vertical Bar Chart With D3 With Labels Using JSON Data

June 9, 2021 by SNK

dynamic bar chart with d3 with labels

Today in this tutorial we are going to learn how to create a responsive and dynamic bar chart with d3. I saw a lot of tutorials out there in d3 but never got any of them which suits my needs for creating my reporting in bar visualization. It would be the exactly same replica of the d3 vertical bar chart like the image above.

So I have created my own responsive yet d3 vertical bar chart with labels with labels of my own configurations so, that I can switch data on demand without having to change anything. So, today here we are going to create a d3 bar chart with json data which we are going to feed such a way that you can manage your data very well while drawing it.

Let’s get started

What are we going todo ?

  1. Create a HTML file with Bootstrap CSS and import D3 from CDN
  2. Create an div with row and col-md-12 class and div with some id which will be using later to draw inside it using d3.
  3. Finally we are going to create use d3 to create a chart using JavaScript and features provided by the d3 library.

How to Draw Dynamic Bar Chart With D3

So, as we are going to draw everything inside the <script></script> , I am going to write everything in comments after step number 3.

If you don’t have time and want to get the copy of this tutorial, you can get it from my GitHub link here. Let’s move ahead with step 1.

Step 1 –  Creating an HTML file with default Bootstrap start layout and import D3 V6 from CDN

However, we dont need bootstrap while drawing a chart. We are solely going to use D3 library in order to manipulate DOM and create the SVG, but i am kind of lazy creating layouts  to align the div properly in the center. So, I know you too are lazy so, you just have to import these two inside your head tag.

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://d3js.org/d3.v6.min.js"></script>

Step 2 – Creating a DIV element with unique ID

We are going to create an div element with an id called svg-container which we will wrap around with bootstrap container & its row grid classes to make it align center, where we will start manipulating the DOM and draw the chart.

Bootstrap container element
1
2
3
4
5
6
7
<div class="container-fluid">
        <div class="row">
            <div class="col-md-12">
                <div id="svg-container"></div>
            </div>
        </div>
    </div>

Step 3 – Let’s Start Drawing the Chart

So I assume that you are inside your <script></script> tag and we are going to start drawing our chart using the D3 library. So, first let’s create some of the input parameters for the bar chart so that we can make it highly configurable.

  1. Define height of the bar chart. We don’t need width as we are going take the dynamic width of screen which will be 100% based on screen size.
  2. Define margin from all the direction like top, left, down and right.
  3. Inner padding which we are going to use it to add spaces between the bars of an chart
  4. Outer padding which we are going to add  spaces at the beginning of starting and end of bar chart.
  5. Series inner padding to adjust the bars spaces inside the single x-axis group.
  6. Rest of them are bar colors, labels that you want to keep vertically aligned with the x-axis.
  7. Finally we are going to create a function called createChart() inside which we were going to construct a chart.

It seems over whelming at first but you will soon understand as we constantly proceed into the code and view their comments. So let’s create some input parameters. These input parameters will be more clear when you start to see its usage.

Bar Chart Input Parameters
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
// Bar Chart params (configurable)
    const input_params = {
        height: 300,
        margin: { top: 10, left: 50, right: 10, bottom: 10 },
        xScaleLabelHeight: 25,
        innerPadding: 0.1,
        outerPadding: 0.1,
        seriesInnerPadding: 0.1,
        reduceBarWidth: 0,
        barColors: ['#00aeef', '#f98e2b', '#7C77AD'],
        yAxisLabel: 'No of Population',
    };

JSON Data

JSON Data
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const chart_data = [
        {
            group: 'United States',
            series: [
                { name: 'Total population', value: 64 },
                { name: 'Infected population', value: 5 },
                { name: 'Safe population', value: 79 }
            ]
        },
        {
            group: 'India',
            series: [
                { name: 'Total population', value: 100 },
                { name: 'Infected population', value: 80 },
                { name: 'Safe population', value: 20 }
            ]
        },
        {
            group: 'China',
            series: [
                { name: 'Total population', value: 100 },
                { name: 'Infected population', value: 20 },
                { name: 'Safe population', value: 80 }
            ]
        }
    ];

Drawing Bar Chart

Drawing an Vertical Bar Chart
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
const createChart = (chart_data) => {
        // create svg
        d3.select('svg').remove();
        svg = d3.select('#svg-container')
            .append('svg')
            .attr('width', '100%')
            .attr('height', input_params.height)
            .append('g')
            .attr('transform', 'translate(0,0)')
            .attr('class', 'vertical-bar-chart');
 
 
        // set left and right margin for the barchart container    
        const margin = {
            top: input_params.margin.top,
            right: input_params.margin.right,
            bottom: input_params.xScaleLabelHeight,
            left: input_params.margin.left
        };
 
        // Give some space for label before the yAxis so it does'nt get overlapped
        margin.left = input_params.yAxisLabel ? input_params.margin.left + 20 : margin.left
 
        // Get the actual Width of the container (Returns the dynamic width depends upon the screen resolution)
        const width = d3.select('#svg-container').node().getBoundingClientRect().width - margin.left - margin.right;
        let height = input_params.height - margin.top - margin.bottom;
 
        // prepare bar chart domain
        const prepareDomainBar = () => {
            let domain = null;
            if (input_params.domain) {
                domain = input_params.domain;
            } else {
                const bar_values = [].concat(...chart_data.map(item => item.series.map(j => j.value)));
                domain = [(d3.min(bar_values) > 0 ? 0 : d3.min(bar_values)), (d3.max(bar_values) * 1.01)];
            }
            input_params.domain = domain;
            return domain;
        }
 
        // Fetching the group from the data array to create x-axis
        const groups = chart_data.map(item => item.group);
        const xAxisGroupLabels = chart_data.length > 0 ? chart_data[0].series.map(item => item.name) : [];
        const domain = prepareDomainBar();
 
        // Setting up bandscale for spacing groups
        const getBandScale = (domain, range, innerPadding, outerPadding) => {
            const scale = d3.scaleBand()
                .range(range)
                .domain(domain)
                .paddingInner(input_params.innerPadding)
                .paddingOuter(input_params.outerPadding);
            // Naming it as a Scale of type BAND
            scale.type = 'BAND';
            return scale;
        }
 
        // Get bandscale
        const x0 = getBandScale(groups, [0, width], input_params.innerPadding, input_params.outerPadding).round(true);
        const x1 = getBandScale(xAxisGroupLabels, [0, x0.bandwidth()], input_params.seriesInnerPadding, input_params.outerPadding).round(true);
 
        // create chart group
        let chart = svg.selectAll('g.chart').data([{}]);
        chart = chart.enter()
            .append('g')
            .attr('class', 'chart')
            .merge(chart)
            .attr('transform', `translate(${margin.left}, ${margin.top})`);
 
        // create another chart group wrapper inside chart group (We may not be needing it but i like to be everything clean)
        let chartGroup = chart.selectAll('g.chart-wrapper').data([{}]);
        chartGroup = chartGroup.enter()
            .append('g')
            .attr('class', 'chart-wrapper')
            .merge(chartGroup)
            .attr('transform', 'translate(0, 0)');
 
        // Create X-AXIS
        const xAxisGroup = chartGroup.selectAll('g.x-axis').data([{}]);
        xAxisGroup.enter()
            .append('g')
            .attr('class', 'x-axis')
            .merge(xAxisGroup)
            .attr('transform', `translate(0, ${height})`)
            .call(d3.axisBottom(x0))
            .selectAll('text')
            .style("text-anchor", "middle");
 
        // create bar group wrappers to create bars    
        let barGroupWrapper = chartGroup.selectAll('g.bar-group-wrapper').data([{}]);
        barGroupWrapper.exit().remove();
        barGroupWrapper = barGroupWrapper.enter()
            .append('g')
            .attr('class', 'bar-group-wrapper')
            .merge(barGroupWrapper);
 
        // create bar group
        let barGroup = barGroupWrapper.selectAll('g.bar-group').data(chart_data);
        const dataLength = chart_data[0].series.length;
        barGroup.exit().remove();
        barGroup = barGroup.enter()
            .append('g')
            .attr('class', 'bar-group')
            .merge(barGroup)
            .attr('transform', d => `translate(${input_params.reduceBarWidth ? (dataLength > 1
                ? input_params.reduceBarWidth / dataLength
                : input_params.reduceBarWidth * 1.5) : x0(d.group)}, 0)`);
 
 
        // Prepare Y-AXIS
        const y = d3.scaleLinear().domain(domain).nice().rangeRound([height, 0]);
        let yAxisGroup = chartGroup.selectAll('g.y-axis').data([{}]);
        yAxisGroup = yAxisGroup.enter()
            .append('g')
            .attr('class', 'y-axis')
            .merge(yAxisGroup)
            .call(d3.axisLeft(y));
 
        // yAxis Top Unit Text
        const axisUnitText = yAxisGroup.selectAll('text.axis-unit').data([{}]);
        axisUnitText.enter()
            .append('text')
            .attr('class', 'axis-unit')
            .merge(axisUnitText)
            .attr('x', 5)
            .attr('y', y(y.ticks().pop()))
            .attr('dy', '0.32rem')
            .attr('text-anchor', 'start')
            .style('fill', '#222')
            .text(input_params.yAxisLabel)
 
        // SET Y-AXIS LABEL        
        let yAxisLabelSelection = svg.selectAll('text.y-axis-label-temp').data([chart_data.yAxisLabel]);
        yAxisLabelSelection = yAxisLabelSelection.enter()
            .append('text')
            .attr('class', 'y-axis-label-temp')
            .attr('y', 10)
            .attr('x', -height / 2)
            .attr('dy', '1em')
            .attr('transform', 'rotate(-90)')
            .style('text-anchor', 'middle')
            .merge(yAxisLabelSelection);
        yAxisLabelSelection.text(input_params.yAxisLabel);
 
        const barRects = barGroup.selectAll('rect.bar').data(d => d.series.map(item => item));
        barRects.enter()
            .append('rect')
            .attr('class', 'bar')
            .attr('width', x1.bandwidth())
            .attr('height', d => height - y(d.value))
            .attr('x', d => x1(d.name))
            .attr('y', d => y(d.value))
            .attr('fill', (d, i) => input_params.barColors[i]);
    }

Now, all you can do is call the createChart(param) function and pass the JSON data as the parameter to see the rendered chart. Only the last thing you have do us now to set the resize event when the browser window is resized it will automatically adjust with the screen making it a responsive yet dynamic bar chart with d3.

window.addEventListener('resize', (event) => createChart(chart_data));

If you want to make some changes you just have to prepare the correct data and feed it into the createChart() function. Also the the labels, margin, padding and color of the bar chart will adjust automatically with the changes your have kept inside the input_params object.

That’s it it. So you will have cool looking bar chart with lots of configurable options. The picture attached with this post is the exactly the type of image that you will get while using the references of my codes.

SHARE ON
Buffer

Enjoyed this article?

Like us on

D3 D3

Avatar for SNK

About SNK

Hello Welcome to my Blog. I develop Websites Using Laravel Framwork & WordPress. Get Latest updates on Facebook | Twitter

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Get Connected !! With Us

TOP POSTS

  • How to Setup Spring MVC Project From Scratch in Intellij IDEA ?
  • Spring Configuration Class in JAVA [Class VS XML Config]
  • Annotations Based Configuration in Spring Framework
  • How to Configure Spring Framework with XML Configurations?
  • How to Remove Project Name from URL in JSP Project in Intellij IDEA ?

TUTORIALS TILL DATE

  • September 2022 (6)
  • June 2021 (7)
  • October 2020 (5)
  • September 2020 (6)
  • September 2018 (14)
  • August 2018 (3)
  • July 2018 (4)
  • March 2018 (8)
  • February 2018 (5)
  • January 2018 (1)
  • December 2017 (2)
  • August 2017 (8)
  • May 2017 (1)
  • April 2017 (1)
  • March 2017 (4)
  • February 2017 (3)
  • January 2017 (4)

CATEGORIES

  • Angular (2)
  • CSS3 (3)
  • D3 (3)
  • HTML5 (7)
  • JAVA (11)
  • Javascript (20)
  • jQuery (8)
  • Laravel (35)
  • Others (3)
  • PHP (11)
  • Spring (2)
  • WordPress (10)

Top Categories

  • Angular
  • CSS3
  • D3
  • HTML5
  • JAVA
  • Javascript
  • jQuery
  • Laravel
  • Others
  • PHP
  • Spring
  • WordPress

Get in Touch

DMCA.com Protection Status

Recent Articles

  • How to Setup Spring MVC Project From Scratch in Intellij IDEA ?
  • Spring Configuration Class in JAVA [Class VS XML Config]
  • Annotations Based Configuration in Spring Framework
  • How to Configure Spring Framework with XML Configurations?
  • How to Remove Project Name from URL in JSP Project in Intellij IDEA ?

© 2012-22 CodeIn House.  •  All Rights Reserved.