title: How to center align text labels on bar chart using d3js
body: <p>I've the following bar chart which is built using <code>d3</code>. I'm unable to center the bar labels over the bars. Eg. <code>40%</code> is not center aligned to the bar.</p>
  
  <p>Snippet:</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>var margin = {
    top: 10,
    right: 0,
    bottom: 58,
    left: 40
  };
  var width = 400 - margin.left - margin.right;
  var height = 400 - margin.top - margin.bottom;
  var barWidth = 40;
  
  var graph;
  var xScale;
  var yScale;
  var dataSet;
  
  dataSet = [{
    desc: 'test1',
    val: 40
  }, {
    desc: 'some dummy text here',
    val: 120
  }];
  
  xScale = d3.scaleBand()
    .domain(dataSet.map(function(d) {
      return d.desc;
    }))
    .range([0, width]);
  
  yScale = d3.scaleLinear()
    .range([height, 0])
    .domain([0, 1.15 * d3.max(dataSet, function(d) {
      return d.val;
    })]);
  
  graph = d3.select("#graph")
    .append("svg")
    .attr("class", "bar-chart")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  
  
  graph.append("g")
    .attr("class", "x-scale")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(xScale))
    .selectAll(".tick text")
    .call(wrap, xScale.bandwidth());
  
  graph.append("g")
    .attr("class", "y-scale")
    .call(d3.axisLeft(yScale).tickPadding(10));
  
  graph
    .append("g")
    .attr('class', 'graph-placeholder')
    .selectAll("rect")
    .data(dataSet)
    .enter()
    .append("rect")
    .attr("class", "bar1")
    .attr("height", height)
    .attr("width", barWidth)
    .attr('x', d =&gt; xScale(d.desc) + (xScale.bandwidth() - barWidth) / 2);
  
  graph
    .append("g")
    .attr('class', 'graph-main')
    .selectAll("bar1")
    .data(dataSet)
    .enter()
    .append("rect")
    .attr("class", "bar2")
    .attr('x', d =&gt; xScale(d.desc) + (xScale.bandwidth() - barWidth) / 2)
    .attr("y", function(d) {
      return yScale(d.val);
    })
    .attr("height", function(d) {
      return height - yScale(d.val);
    })
    .attr("width", barWidth);
  
  
  graph
    .append("g")
    .attr('class', 'bar-label')
    .selectAll("text")
    .data(dataSet)
    .enter()
    .append("text")
    .text(d =&gt; d.val+ '%')
    .attr("y", function(d) {
      return yScale(d.val) - 5;
    }).attr('x', function(d) {
      return xScale(d.desc) + ((xScale.bandwidth() - barWidth) / 2);
    });
  
  function wrap(text, width) {
    text.each(function() {
      var text = d3.select(this),
        words = text.text().split(/\s+/).reverse(),
        word,
        line = [],
        lineNumber = 0,
        lineHeight = 1,
        y = text.attr("y"),
        dy = parseFloat(text.attr("dy")),
        tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
      while (word = words.pop()) {
        line.push(word);
        tspan.text(line.join(" "));
        if (tspan.node().getComputedTextLength() &gt; width) {
          line.pop();
          tspan.text(line.join(" "));
          line = [word];
          tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
        }
      }
    });
  }</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>.bar2 {
    fill: steelblue;
  }
  
  .bar1 {
    fill: #f2f2f2;
  }
  
  text {
    font-size: 12px;
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"&gt;&lt;/script&gt;
  &lt;div class="container"&gt;
    &lt;div id="graph"&gt;&lt;/div&gt;
  &lt;/div&gt;</code></pre>
  </div>
  </div>
  </p>