1

I am making a vertical bar chart with D3 successfully. I further want the value of frequency to lie on the top of each rectangle. I am able to get the complete structure and rectangles. But, I cannot see my values residing on top of the bars. Can someone help out?

SNIPPET:

<html>

<head>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.12/angular.min.js"></script> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>

</head> 

<body ng-app="myApp" ng-controller="myCtrl"> 

    <svg></svg>

    <script>

        //module declaration 
        var app = angular.module('myApp',[]);

        //Controller declaration
        app.controller('myCtrl',function($scope){

            $scope.svgWidth = 800;//svg Width
            $scope.svgHeight = 500;//svg Height 

            //Data in proper format 
            var data = [
                  {"letter": "A","frequency": "5.01"},
                  {"letter": "B","frequency": "7.80"},
                  {"letter": "C","frequency": "15.35"},
                  {"letter": "D","frequency": "22.70"},
                  {"letter": "E","frequency": "34.25"},
                  {"letter": "F","frequency": "10.21"},
                  {"letter": "G","frequency": "7.68"},
            ];

                //removing prior svg elements ie clean up svg 
                d3.select('svg').selectAll("*").remove();

                //resetting svg height and width in current svg 
                d3.select("svg").attr("width", $scope.svgWidth).attr("height", $scope.svgHeight);

                //Setting up of our svg with proper calculations 
                var svg = d3.select("svg");
                var margin = {top: 20, right: 20, bottom: 30, left: 40};
                var width = svg.attr("width") - margin.left - margin.right;
                var height = svg.attr("height") - margin.top - margin.bottom;

                //Plotting our base area in svg in which chart will be shown 
                var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

                //X and Y scaling 
                var x = d3.scaleBand().rangeRound([0, width]).padding(0.4);
                var y = d3.scaleLinear().rangeRound([height, 0]);

                x.domain(data.map(function(d) { return d.letter; }));
                y.domain([0, d3.max(data, function(d) { return +d.frequency; })]);

                //Final Plotting 

                //for x axis 
                g.append("g")
                    .call(d3.axisBottom(x))
                    .attr("transform", "translate(0," + height + ")");

                //for y axis 
                g.append("g")
                    .call(d3.axisLeft(y))
                    .append("text").attr("transform", "rotate(-90)").attr("text-anchor", "end");

                  //for rectangles 
                  g.selectAll(".bar")
                    .data(data)
                    .enter()
                    .append("rect")
                    .attr("class", "bar")
                    .attr("x", function(d) { return x(d.letter); })
                    .attr("y", function(d) { return y(d.frequency); })
                    .attr("width", x.bandwidth())
                    .attr("height", function(d) { return height - y(d.frequency); });

               g
                .selectAll(".bar")
                .append("text")
                .attr("dy", ".75em")
                .attr("y", function(d) { return y(d.frequency); })
                .attr("x", function(d) { return x(d.letter); })
                .attr("text-anchor", "middle")
                .text(function(d) { return y(d.frequency); });


        });

    </script> 

</body> 

</html> 

RESULT:

enter image description here

DOM:

enter image description here

As, can be seen my text is added in dom per each rectangle, but cannot be seen. Pls help them being seen on top of rectangles.

Deadpool
  • 7,811
  • 9
  • 44
  • 88

2 Answers2

4

The problem here is simple: you cannot append a text element inside a rect element.

The text element will show up when you inspect the DOM, but this doesn't mean that it works, and no text will be actually painted in the SVG.

So, it should be:

 g.selectAll(".text")
      .data(data)
      .enter()
      .append("text")
      .attr("dy", ".75em")
      .attr("y", function(d) { return y(d.frequency); })
      .attr("x", function(d) { return x(d.letter); })
      .attr("text-anchor", "middle")
      .text(function(d) { return d.frequency; });

Note that I changed

.text(function(d) { return y(d.frequency); });

which makes no sense, to:

.text(function(d) { return d.frequency; });

Here is a demo with your code:

var data = [{
    "letter": "A",
    "frequency": "5.01"
}, {
    "letter": "B",
    "frequency": "7.80"
}, {
    "letter": "C",
    "frequency": "15.35"
}, {
    "letter": "D",
    "frequency": "22.70"
}, {
    "letter": "E",
    "frequency": "34.25"
}, {
    "letter": "F",
    "frequency": "10.21"
}, {
    "letter": "G",
    "frequency": "7.68"
}, ];


//Setting up of our svg with proper calculations 
var svg = d3.select("body")
    .append("svg").attr("width", 500).attr("height", 300);
var margin = {
    top: 40,
    right: 20,
    bottom: 40,
    left: 40
};
var width = svg.attr("width") - margin.left - margin.right;
var height = svg.attr("height") - margin.top - margin.bottom;

//Plotting our base area in svg in which chart will be shown 
var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

//X and Y scaling 
var x = d3.scaleBand().rangeRound([0, width]).padding(0.4);
var y = d3.scaleLinear().rangeRound([height, 0]);

x.domain(data.map(function(d) {
    return d.letter;
}));
y.domain([0, d3.max(data, function(d) {
    return +d.frequency;
})]);

//Final Plotting 

//for x axis 
g.append("g")
    .call(d3.axisBottom(x))
    .attr("transform", "translate(0," + height + ")");

//for y axis 
g.append("g")
    .call(d3.axisLeft(y))
    .append("text").attr("transform", "rotate(-90)").attr("text-anchor", "end");

//for rectangles 
g.selectAll(".bar")
    .data(data)
    .enter()
    .append("rect")
    .attr("class", "bar")
    .attr("x", function(d) {
        return x(d.letter);
    })
    .attr("y", function(d) {
        return y(d.frequency);
    })
    .attr("width", x.bandwidth())
    .attr("height", function(d) {
        return height - y(d.frequency);
    });

g.selectAll(".text")
    .data(data)
    .enter()
    .append("text")
    .attr("dy", ".75em")
    .attr("y", function(d) {
        return y(d.frequency) - 16;
    })
    .attr("x", function(d) {
        return x(d.letter) + x.bandwidth() / 2;
    })
    .attr("text-anchor", "middle")
    .text(function(d) {
        return d.frequency;
    });
<script src="https://d3js.org/d3.v4.min.js"></script>
Graham
  • 7,431
  • 18
  • 59
  • 84
Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
  • Its working nicely. One more thing how can we rotate the text as (-90)deg in this example ? – Suresh May 25 '18 at 11:18
1

The text element should not be inside the rect element. Just append them to your g element instead.

Matthew Wilcoxson
  • 3,432
  • 1
  • 43
  • 48